Token Endpoint
The token endpoint is where your application exchanges credentials for access tokens. It supports several grant types: authorization code (for interactive user logins), client credentials (for service-to-service calls), refresh token (for extending sessions without re-prompting the user), and token exchange (for delegation and impersonation). This endpoint is defined by RFC 6749 — the OAuth 2.0 Authorization Framework, the core specification that defines the authorize and token endpoints and the standard grant types.
POST /orgs/{orgId}/api/v1/oauth/token
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
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | authorization_code |
code | Yes | The authorization code from the callback |
redirect_uri | Yes | Must match the URI used in the authorization request |
client_id | Yes | Your application's client ID |
client_secret | Confidential clients | Your application's client secret (not needed for public clients with PKCE) |
code_verifier | If PKCE used | The original code_verifier that matches the code_challenge |
Example Request
curl -X POST https://app.lumoauth.dev/orgs/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. Use this for server-to-server communication, cron jobs, background workers, or AI agents.
Request Parameters
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | client_credentials |
client_id | Yes | Your application's client ID |
client_secret | Yes | Your application's client secret |
scope | No | Space-separated scopes (defaults to the client's registered scopes) |
Example Request
curl -X POST https://app.lumoauth.dev/orgs/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
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | refresh_token |
refresh_token | Yes | The refresh token from a previous token response |
client_id | Yes | Your application's client ID |
client_secret | Confidential clients | Your application's client secret |
Example Request
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/oauth/token \
-u "CLIENT_ID:CLIENT_SECRET" \
-d "grant_type=refresh_token" \
-d "refresh_token=dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
LumoAuth rotates refresh tokens. Each time you use a refresh token, a new one is issued and the old one is invalidated. If the old token is used again, the entire token family is revoked — this detects theft.
Token Exchange Grant
Token exchange (RFC 8693 — OAuth 2.0 Token Exchange: lets a service swap one token for another, the mechanism behind delegation and impersonation) enables scenarios like:
- Impersonation — a service acts as a user.
- Delegation — a service acts on behalf of a user, with an audit trail.
- Workload identity — exchange external tokens (AWS, Kubernetes) for LumoAuth tokens.
Request Parameters
| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | urn:ietf:params:oauth:grant-type:token-exchange |
subject_token | Yes | The token representing the subject (user or service) |
subject_token_type | Yes | urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:jwt |
actor_token | For delegation | Token of the service acting on behalf of the subject |
actor_token_type | If actor_token | Type of the actor token |
Delegation Example
Service B acts on behalf of User A. The resulting token has an act claim that records the chain of identity.
curl -X POST https://app.lumoauth.dev/orgs/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 acting 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"
}
| Error | Description |
|---|---|
invalid_request | Missing or invalid parameters |
invalid_client | Client authentication failed |
invalid_grant | Authorization code or refresh token is invalid or expired |
invalid_scope | Requested scope is not allowed for this client |
unsupported_grant_type | The grant type is not supported |
Related
- Authorization endpoint
- PKCE
- Token formats
- Token revocation — RFC 7009, OAuth 2.0 Token Revocation: endpoint to invalidate an access or refresh token