Skip to main content

Token Formats

LumoAuth issues access tokens in two formats: JWT (default) and opaque. JWTs are self-contained and can be validated offline by any resource server holding the issuer's public key; opaque tokens are random strings that must be validated by calling the introspection endpoint.

JWT Access Tokens

JWT access tokens follow RFC 9068 — JWT Profile for OAuth 2.0 Access Tokens: the standard set of claims for JWT access tokens. A JWT (RFC 7519 — JSON Web Token: a signed JSON payload) can be verified offline with the issuer's public key from the JWKS endpoint.

JWKS endpoint:
GET /orgs/{orgId}/api/v1/.well-known/jwks.json

Supported Signing Algorithms

AlgorithmKey type
RS256RSA 2048
RS384RSA 2048
RS512RSA 2048
ES256EC P-256
ES384EC P-384
PS256RSA-PSS

Standard JWT Claims

ClaimDescription
issIssuer (https://app.lumoauth.dev/orgs/{orgId})
subSubject — user or agent ID
audAudience — client ID or resource server
expExpiration time (Unix timestamp)
iatIssued-at time
jtiUnique token ID
client_idOAuth client that requested the token
scopeSpace-separated granted scopes
token_useaccess (RFC 9068)

Extension Claims

ClaimDescription
rolesArray of role names (requires roles scope)
groupsArray of group names (requires groups scope)
permissionsArray of permission slugs (requires permissions scope)
emailUser email (requires email scope)
organizationOrganization slug
Decoded JWT Access Token
{
"iss": "https://app.lumoauth.dev/orgs/acme-corp",
"sub": "01JF3KABCDE...",
"aud": ["https://api.example.com"],
"exp": 1753920000,
"iat": 1753916400,
"jti": "01JF3KXYZ...",
"client_id": "app_client_01jf3k...",
"scope": "openid profile email roles",
"token_use": "access",
"email": "john@acme.com",
"roles": ["admin"],
"organization": "acme-corp"
}

Opaque Access Tokens

Opaque tokens are random strings with no embedded claims. They must be validated via the Introspection Endpoint.

DPoP

LumoAuth supports DPoP-bound access tokens to prevent token replay. DPoP is defined by RFC 9449 — Demonstrating Proof-of-Possession: binds an access token to a client-held key. Each request includes a short-lived proof JWT signed with that key; a stolen token alone is useless from another host.

Include a DPoP header with a signed JWT proof in all requests when using DPoP tokens.

DPoP Token Request
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7...}" \
-d "grant_type=authorization_code" \
-d "code=SplxlOBeZQQYbYS6WxSbIA" \
-d "..."

Token Lifetimes

Default token lifetimes (configurable per client in the portal):

Token typeDefault lifetime
Access token3600 s (1 hour)
ID token3600 s (1 hour)
Refresh token2592000 s (30 days)
Authorization code60 s