Skip to main content

Token Endpoint

The token endpoint is where your application exchanges credentials for access tokens. It supports multiple grant types for different authentication scenarios.

POST /t/\{tenantSlug\}/api/v1/oauth/token

Server-to-Server

For machine-to-machine communication, use the Client Credentials grant. No user interaction is needed.

Authorization Code Grant

Exchange an authorization code (received from the authorize endpoint) for access and refresh tokens. This is the most common flow for user authentication.

Request Parameters

ParameterRequiredDescription
grant_typeYesauthorization_code
codeYesThe authorization code from the callback
redirect_uriYesMust match the URI used in the authorization request
client_idYesYour application's client ID
client_secretConfidential clientsYour application's client secret (not needed for public clients with PKCE)
code_verifierIf PKCE usedThe original code_verifier that matches the code_challenge

Example Request

curl -X POST https://app.lumoauth.dev/t/acme-corp/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=SplxlOBeZQQYbYS6WxSbIA" \
-d "redirect_uri=https://myapp.com/callback" \
-d "client_id=abc123def456" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Response

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
"scope": "openid profile email",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Client Credentials Grant

Authenticate your application as itself, without a user. Perfect for server-to-server communication, cron jobs, or background workers.

Great for AI Agents

Client Credentials is the simplest way to authenticate AI agents and background services that don't act on behalf of a specific user.

Request Parameters

ParameterRequiredDescription
grant_typeYesclient_credentials
client_idYesYour application's client ID
client_secretYesYour application's client secret
scopeNoSpace-separated scopes (defaults to client's registered scopes)

Example Request

curl -X POST https://app.lumoauth.dev/t/acme-corp/api/v1/oauth/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-d "grant_type=client_credentials" \
-d "scope=read:reports write:data"

Response

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:reports write:data"
}

Refresh Token Grant

Get a new access token using a refresh token, without requiring the user to log in again. Refresh tokens typically last 30 days.

Request Parameters

ParameterRequiredDescription
grant_typeYesrefresh_token
refresh_tokenYesThe refresh token from a previous token response
client_idYesYour application's client ID
client_secretConfidential clientsYour application's client secret

Example Request

curl -X POST https://app.lumoauth.dev/t/acme-corp/api/v1/oauth/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-d "grant_type=refresh_token" \
-d "refresh_token=dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
Refresh Token Rotation

LumoAuth implements refresh token rotation for security. Each time you use a refresh token, a new one is issued and the old one is invalidated.

Token Exchange Grant (RFC 8693)

Exchange one token for another. This enables advanced scenarios like:

  • Impersonation: A service acts as a user
  • Delegation: A service acts on behalf of a user, with audit trail
  • Workload Identity: Exchange external tokens (AWS, K8s) for LumoAuth tokens

Request Parameters

ParameterRequiredDescription
grant_typeYesurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenYesThe token representing the subject (user or service)
subject_token_typeYesurn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:jwt
actor_tokenFor delegationToken of the service acting on behalf of the subject
actor_token_typeIf actor_tokenType of the actor token

Delegation Example

Service B acts on behalf of User A. The resulting token has an act claim showing the chain of identity.

curl -X POST https://app.lumoauth.dev/t/acme-corp/api/v1/oauth/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=USER_A_ACCESS_TOKEN" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
-d "actor_token=SERVICE_B_ACCESS_TOKEN" \
-d "actor_token_type=urn:ietf:params:oauth:token-type:access_token"

Resulting JWT Claims

{
"sub": "user_a",
"act": {
"sub": "service_b"
},
"iss": "https://app.lumoauth.dev",
"exp": 1704067200
}

The act (actor) claim indicates that service_b is performing actions on behalf of user_a. This creates a complete audit trail.

Error Responses

Token endpoint errors follow the OAuth 2.0 specification:

{
"error": "invalid_grant",
"error_description": "Authorization code is invalid, expired, or revoked"
}
ErrorDescription
invalid_requestMissing or invalid parameters
invalid_clientClient authentication failed
invalid_grantAuthorization code or refresh token is invalid/expired
invalid_scopeRequested scope is not allowed for this client
unsupported_grant_typeThe grant type is not supported