Authentication
Get a bearer token for HTTP calls, as a person at a terminal or as a workload such as a CI job.
Every API call carries a short-lived access token, sent as
Authorization: Bearer <token>. How you get one depends on who is calling. A
person runs spiral login and approves the sign-in in a browser. A CI job
or a service signs in as a workload, ideally with
the identity token its platform already provides, such as GitHub Actions or
Google Cloud, so there is no secret to store or rotate.
The authorization server at https://auth.spiraldb.com issues tokens, and
its OAuth endpoints follow the OAuth 2.0 specifications. Clients can read its
metadata from /.well-known/oauth-authorization-server
(RFC 8414). The
Authentication service describes identities
and every endpoint.
Choose a method
| Caller | Method | Token endpoint |
|---|---|---|
| A person at a terminal | Device authorization grant (RFC 8628) through spiral login | auth.spiraldb.com |
| A workload with a registered key pair | Client credentials with a JWT client assertion (RFC 7523 section 2.2) | auth.spiraldb.com |
| A workload on GitHub Actions, Google Cloud, AWS, Modal, Kubernetes, or another OpenID Connect issuer | JWT-bearer grant with the platform's identity token (RFC 7523 section 2.1) | auth.spiraldb.com |
A workload with a client secret (spw_...) | Client credentials with a client secret | api.spiraldb.dev |
Prefer a key pair or a platform identity for workloads. A client secret is a
long-lived shared secret that anyone who copies it can use, and a workload
that signs in with one receives twelve-hour storage tokens instead of
fifteen-minute ones (see Security). Once the workload uses
another method, revoke its secret with spiral workloads revoke-secret.
Sign in as a person
Sign in with the CLI. It uses the device flow: it prints a URL and a code, and waits until you approve the code in a browser:
spiral login --show-tokenThe last line of the output is the access token, so a script can keep just that line and use it for direct HTTP calls:
export SPIRAL_TOKEN="$(spiral login --show-token | tail -n 1)"
curl https://api.spiraldb.dev/v1/organizations/me \
-H "Authorization: Bearer $SPIRAL_TOKEN"The CLI caches the tokens and refreshes them when they expire. Use
--org <ORG> to select an Organization. See
spiral login.
Refresh tokens are bound to the client that signed in. Use the same client_id
when refreshing or revoking one, and store each replacement token returned by
the server. First-party refresh tokens also preserve the Organization selected
at sign-in. To switch Organizations, include organization_id in the refresh
request; the replacement token preserves the new Organization on later refreshes.
Older first-party refresh tokens without an Organization are no longer accepted;
run spiral login again if refreshing returns invalid_grant. Disabling a
client prevents new authorizations and token grants.
Sign in as a workload with a key pair
Register the public key of the workload with
spiral workloads keys. Then sign a
short-lived JWT client assertion with the private key, and present it at the
token endpoint:
issandsub: the workload's client ID.aud:https://auth.spiraldb.com/oauth/token.jti: a unique value.exp: a short expiry, such as 60 seconds.- Header
kid: the key ID that you registered.
curl -X POST https://auth.spiraldb.com/oauth/token \
-d grant_type=client_credentials \
-d client_id="$SPIRAL_CLIENT_ID" \
-d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
-d client_assertion="$CLIENT_ASSERTION"The authorization server accepts only signed assertions from workloads. Client secrets go to the API host instead, as the next sections show.
Sign in with a platform identity
Add a trust policy to the workload with
spiral workloads trust. The policy
names the platform identity that can sign in as the workload. Then request an
identity token from the platform with the audience
https://auth.spiraldb.com/oauth/token, and present it as the grant:
curl -X POST https://auth.spiraldb.com/oauth/token \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d client_id="$SPIRAL_WORKLOAD_ID" \
-d assertion="$PLATFORM_TOKEN"The JWT-bearer grant takes no client credentials. The platform token authenticates the request.
Sign in as a workload with a client secret
Client secrets (spw_...) are the older way for a workload to sign in. The
API host still accepts them at its own token endpoint, until they are
removed:
curl -X POST https://api.spiraldb.dev/oauth/token \
-d grant_type=client_credentials \
-d client_id="$SPIRAL_CLIENT_ID" \
-d client_secret="$SPIRAL_CLIENT_SECRET"The response contains access_token and token_type (Bearer). Request a new
token when the token expires.
Exchange a token
The authorization server implements token exchange
(RFC 8693) to narrow a token. A
token names every service it is for in its aud claim: your client's sign-in
or grant decides which, and Spiral registers ScanDAL and the telemetry
collector for its own clients, Applications and Workloads. Before sending a
token to one of those services, a client exchanges it for one that names only
that service, so the service never holds your token for Spiral's API.
Send the token as subject_token and name each service you want as a
resource. No client credentials are needed, because the token is the
credential. The new token keeps every claim of the original, except that:
- it names only the requested resources, each of which the original names;
- it carries the requested
scope, if any, which the original's scope must cover; - it expires no later than the original.
A request for anything else fails with invalid_target or invalid_scope.
You can narrow a narrowed token again. The SDKs narrow tokens for
ScanDAL and telemetry themselves, so most clients
never call the exchange directly.
No token is exchanged for storage: the Storage service obtains storage access as its own client, once your permission on the resource is checked.
Call from a web page
A web page can call Spiral's API with its user's token directly, without a
backend of its own, when the client the token was issued to registered the
page's origin. An Application registers origins with
allowedCorsOrigins, and the user
tokens issued to it carry them, so the API answers the browser without looking the
Application up. The same token reaches the authorization server's token
endpoint and, through a capability,
ScanDAL. Spiral
Studio works this way.
The browser's preflight request always succeeds, because it carries no token.
A request with a valid token from an origin its client did not register is
refused with 403 before any work, and a request without a valid token is
refused with 401; the page can read both refusals. Responses never allow
cookies, so a page reaches the API only with a token it holds, and the scopes
the user granted still limit what that token can do.
Revoke a token
Revoke a token at POST https://auth.spiraldb.com/oauth/revoke
(RFC 7009). Revoking a person's
access token ends the sign-in session, so it can no longer be refreshed, but
the access token already issued keeps working until it expires, at most an
hour later. spiral logout revokes the session and removes the cached
tokens of the CLI.
Errors
The OAuth endpoints of the authorization server return OAuth error bodies:
error and error_description, with status 400, or 401 for
invalid_client. The token endpoint of the API host returns the
message body.