Skip to main content

Tokens

This page describes the tokens the OAuth server issues, how a receiving service validates each one, and how long each one lives.

TokenFormatSigned with
Access tokenA JWT access token (RFC 9068), typ at+jwtThe OAuth signing key
On-behalf-of tokenA JWT-SVID with an obo claimThe JWT-SVID signing key
Transaction TokenA Transaction Token, typ txntoken+jwtThe OAuth signing key
External service tokenWhatever the external service issuesThe external service's key

Access tokens​

Access tokens are for OAuth resource servers, such as MCP servers. Defakto issues them to workloads, with the JWT bearer grant, and to OAuth clients, for people who sign in.

ClaimValue
issThe OAuth issuer
subFor a workload, its SPIFFE ID. For a person, their identifier from the identity provider, which is never a SPIFFE ID.
audThe resource or audience the caller requested
client_idFor a workload, its SPIFFE ID, the same as sub. For a person, the OAuth client's client ID.
scopeThe granted scopes
cnfOnly on a DPoP-bound token: the thumbprint of the bound key, as {"jkt": "<thumbprint>"}
jti, iat, expThe token's ID, issue time, and expiry

For examples of an access token for a workload and for a person, see What your MCP server receives.

Validating an access token​

Validate access tokens as RFC 9068 describes, with these values:

  • Issuer: the OAuth issuer, not the JWT-SVID issuer.
  • Keys: the keys at your trust domain's OAuth JWKS Endpoint, which the OAuth server's metadata also lists as jwks_uri.
  • Audience: your service's identifier, such as your MCP server's URL.

A token with a cnf claim is bound to a key with DPoP. See DPoP.

On-behalf-of tokens​

An on-behalf-of token is a JWT-SVID for the acting workload, with an obo claim that names the user and every workload that acted for the user. It's signed with the trust domain's JWT-SVID key, so it validates with the same SPIFFE libraries and keys as any JWT-SVID. A JWT-SVID without an obo claim is an ordinary JWT-SVID.

{
"iss": "<JWT-SVID issuer>",
"sub": "spiffe://example.com/ns/orders/sa/api",
"aud": "spiffe://example.com/payments",
"exp": 1792598887,
"iat": 1792598587,
"jti": "de0043b7455d66690dbe37ff506d2ef3",
"txn": "b7814530c95811d288ac0390515c61af",
"obo": {
"iss": "https://idp.example.com",
"sub": "alice-8f3k2",
"sub_profile": "user",
"via": "idToken",
"act": {
"iss": "spiffe://example.com",
"sub": "spiffe://example.com/ns/orders/sa/api",
"act": {
"iss": "spiffe://example.com",
"sub": "spiffe://example.com/ns/web/sa/backend"
}
}
}
}
ClaimValue
subThe acting workload's SPIFFE ID
audThe service the token is for
txnA transaction identifier (RFC 8417), created at the first exchange and carried unchanged through every re-exchange
obo.issThe issuer of the identity provider that vouched for the user
obo.subThe user's identifier, from the sub claim of the user's token. See User identifier.
obo.sub_profileuser
obo.viaHow the user arrived: idToken, accessToken, or login. Later exchanges match the token policy's user the same way.
obo.actThe workloads that acted for the user, most recent first, nested as in RFC 8693 section 4.1. The outermost act.sub is always the same as sub.

Custom claims from the token policy appear at the top level.

Validating an on-behalf-of token​

  1. Validate it as a JWT-SVID, with a SPIFFE library or as the JWT-SVID specification describes.
  2. Read the user from obo.sub, and the identity provider from obo.iss. If you expect users from particular identity providers only, check obo.iss.
  3. Read sub, or the outermost obo.act.sub, for the workload calling you, and the nested act entries for the workloads before it.

Like any JWT-SVID, an on-behalf-of token is a bearer token. It names the workload it was issued to, but doesn't prove which workload sent it.

Transaction Tokens​

A Transaction Token records who started a transaction, and travels with every call in the transaction in the Txn-Token HTTP header. See Propagate a user's identity.

ClaimValue
subThe user or workload who started the transaction
audYour trust domain name
txnA transaction identifier
scopeThe transaction's scopes
req_wlThe SPIFFE ID of the workload that requested the Transaction Token
iat, expThe token's issue time and expiry

Validating a Transaction Token​

Validate Transaction Tokens as the Transaction Tokens draft describes, with these values:

External service tokens​

Defakto returns an external service's token exactly as the service's authorization server issued it. The service sets its format, contents, and lifetime, and validates it. Defakto never accepts an external service token back as a token to exchange.

Lifetimes​

A token that Defakto issues lives for 5 minutes, unless a matching token policy rule's maxTTL sets a different lifetime, longer or shorter. Each kind of token has a maximum lifetime. A longer maxTTL is capped at that maximum:

TokenMaximum lifetime
Access token for a workload7 days
Access token for a person7 days
On-behalf-of token24 hours
Transaction Token10 minutes

Every token is also capped by:

  • The remaining lifetime of the credential it was issued from. An access token never outlives the JWT-SVID that the workload presented, or the identity provider's ID token from a sign-in. An on-behalf-of token never outlives the user's token or the on-behalf-of token it was exchanged from. A Transaction Token never outlives the user's token it was exchanged from.
  • A shorter max_ttl from an external authorizer, when one decides the request.

Client authentication doesn't affect the lifetime. A JWT-SVID client assertion, or the X.509-SVID used for mutual TLS, only proves who the caller is. The expiry of that credential doesn't shorten the issued token.