Skip to main content

OAuth 2.0 & OpenID Connect

LumoAuth implements OAuth 2.0 and OpenID Connect (OIDC) as its core authorization and authentication framework. This guide covers grant types, token management, OIDC features, and advanced protocol extensions.


Supported Grant Types

Grant TypeUse CaseRFC
Authorization CodeWeb apps, SPAs (with PKCE)RFC 6749
Authorization Code + PKCEPublic clients (SPAs, mobile)RFC 7636
Client CredentialsMachine-to-machineRFC 6749
Refresh TokenRenew expired access tokensRFC 6749
Device AuthorizationCLI tools, smart TVs, IoTRFC 8628
CIBADecoupled authentication (call center, POS)OIDC CIBA

Authorization Code Flow

The most common flow for web applications:

1. App redirects user to:
/t/{tenantSlug}/api/v1/oauth/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://your-app.com/callback
&scope=openid profile email
&state=random_state

2. User authenticates (login page)

3. LumoAuth redirects back with authorization code:
https://your-app.com/callback?code=AUTH_CODE&state=random_state

4. App exchanges code for tokens:
POST /t/{tenantSlug}/api/v1/oauth/token
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://your-app.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

With PKCE (for Public Clients)

Add PKCE parameters for SPAs and mobile apps:

Step 1: Generate code_verifier and code_challenge

Step 2: Include in authorization request:
&code_challenge=CHALLENGE
&code_challenge_method=S256

Step 3: Include in token request:
&code_verifier=VERIFIER

Client Credentials Flow

For machine-to-machine authentication (no user involved):

curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/token \
-d grant_type=client_credentials \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d scope="api:read api:write"

Refresh Token Flow

Exchange a refresh token for a new access token:

curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=YOUR_REFRESH_TOKEN \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET

OIDC Features

Discovery

Every tenant exposes an OIDC discovery document:

GET /t/{tenantSlug}/.well-known/openid-configuration

Returns endpoints, supported scopes, signing algorithms, and more.

ID Tokens

ID tokens are JWTs containing user identity claims:

{
"iss": "https://your-domain.com/t/acme-corp",
"sub": "user-uuid",
"aud": "client-id",
"exp": 1706403600,
"iat": 1706400000,
"nonce": "random-nonce",
"email": "alice@acme.com",
"name": "Alice Smith",
"email_verified": true
}

UserInfo Endpoint

curl https://your-domain.com/t/{tenantSlug}/api/v1/oauth/userinfo \
-H "Authorization: Bearer {access_token}"

Standard Scopes

ScopeClaims Included
openidsub
profilename, given_name, family_name, picture
emailemail, email_verified
phonephone_number, phone_number_verified
rolesroles array
permissionspermissions array

JWKS Endpoint

Public keys for token verification:

GET /t/{tenantSlug}/.well-known/jwks.json

Advanced Protocol Extensions

DPoP (Demonstration of Proof-of-Possession) - RFC 9449

DPoP binds access tokens to the client's cryptographic key, preventing token theft:

# Include DPoP proof header in token request
curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/token \
-H "DPoP: eyJ..." \
-d grant_type=authorization_code \
-d code=AUTH_CODE \
-d client_id=YOUR_CLIENT_ID

The resulting token is bound to the DPoP key and can only be used with a matching DPoP proof.

PAR (Pushed Authorization Requests) - RFC 9126

Push authorization parameters to the server before redirecting the user:

# Push authorization request
curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/par \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d response_type=code \
-d redirect_uri=https://your-app.com/callback \
-d scope="openid profile"

# Response: {"request_uri": "urn:ietf:params:oauth:request_uri:...", "expires_in": 60}

# Redirect user with the request_uri
/t/{tenantSlug}/api/v1/oauth/authorize?client_id=YOUR_CLIENT_ID&request_uri=urn:ietf:params:oauth:request_uri:...

RAR (Rich Authorization Requests) - RFC 9396

Request fine-grained authorization with structured details:

{
"authorization_details": [
{
"type": "payment_initiation",
"instructedAmount": {"currency": "EUR", "amount": "123.50"},
"creditorName": "Merchant Inc.",
"creditorAccount": {"iban": "DE02..."}
}
]
}

Dynamic Client Registration - RFC 7591

Register OAuth clients programmatically:

curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My App",
"redirect_uris": ["https://my-app.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_post"
}'

CIBA (Client-Initiated Backchannel Authentication)

Initiate authentication from a backend service:

curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/bc-authorize \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET \
-d scope="openid" \
-d login_hint="alice@acme.com" \
-d binding_message="Approve login from call center"

The user receives a push notification or out-of-band prompt to approve.


Token Formats

TokenFormatDescription
Access TokenJWT (RFC 9068)Contains claims, verifiable without server call
Refresh TokenOpaqueMust be exchanged at the token endpoint
ID TokenJWTOIDC identity claims
Authorization CodeOpaqueShort-lived, single-use

Token Revocation

Revoke tokens that are no longer needed:

curl -X POST https://your-domain.com/t/{tenantSlug}/api/v1/oauth/revoke \
-d token=TOKEN_TO_REVOKE \
-d client_id=YOUR_CLIENT_ID \
-d client_secret=YOUR_CLIENT_SECRET