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
| Algorithm | Key type |
|---|---|
RS256 | RSA 2048 |
RS384 | RSA 2048 |
RS512 | RSA 2048 |
ES256 | EC P-256 |
ES384 | EC P-384 |
PS256 | RSA-PSS |
Standard JWT Claims
| Claim | Description |
|---|---|
iss | Issuer (https://app.lumoauth.dev/orgs/{orgId}) |
sub | Subject — user or agent ID |
aud | Audience — client ID or resource server |
exp | Expiration time (Unix timestamp) |
iat | Issued-at time |
jti | Unique token ID |
client_id | OAuth client that requested the token |
scope | Space-separated granted scopes |
token_use | access (RFC 9068) |
Extension Claims
| Claim | Description |
|---|---|
roles | Array of role names (requires roles scope) |
groups | Array of group names (requires groups scope) |
permissions | Array of permission slugs (requires permissions scope) |
email | User email (requires email scope) |
organization | Organization slug |
{
"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.
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 type | Default lifetime |
|---|---|
| Access token | 3600 s (1 hour) |
| ID token | 3600 s (1 hour) |
| Refresh token | 2592000 s (30 days) |
| Authorization code | 60 s |
Related
- Introspection — required for opaque tokens, optional for JWTs
- FAPI 2.0 — DPoP and sender-constrained tokens
- Resource indicators — set the
audclaim on issued tokens