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. RFC 6749 — OAuth 2.0 Authorization Framework defines the authorize and token endpoints and the standard grant types (authorization code, client credentials, refresh token, etc.); OIDC adds an identity layer on top with an id_token and userinfo endpoint. This guide covers grant types, token management, OIDC features, and 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:
/orgs/{orgId}/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 /orgs/{orgId}/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/orgs/{orgId}/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/orgs/{orgId}/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 organization exposes an OIDC discovery document:

GET /orgs/{orgId}/.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/orgs/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/orgs/{orgId}/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

JWKS (JSON Web Key Set, RFC 7517) is a JSON document listing the public keys a service uses to sign its JWTs. Clients fetch it to verify access and ID tokens.

GET /orgs/{orgId}/.well-known/jwks.json

Advanced Protocol Extensions

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

RFC 9449 — DPoP binds an access token to a key pair held by the client. Every API call must include a fresh signed proof, so a stolen bearer token alone cannot be replayed by an attacker.

# Include DPoP proof header in token request
curl -X POST https://your-domain.com/orgs/{orgId}/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

RFC 9126 — Pushed Authorization Requests lets the client POST the authorization parameters to the server first and then redirect the user with a short opaque request_uri. This keeps sensitive parameters out of the browser address bar and front channel.

# Push authorization request
curl -X POST https://your-domain.com/orgs/{orgId}/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
/orgs/{orgId}/api/v1/oauth/authorize?client_id=YOUR_CLIENT_ID&request_uri=urn:ietf:params:oauth:request_uri:...

RAR (Rich Authorization Requests) — RFC 9396

RFC 9396 — Rich Authorization Requests extends OAuth with a structured authorization_details parameter so the client can request fine-grained, typed authorization (for example a specific payment amount or a specific set of account operations) instead of only flat scope strings.

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

Dynamic Client Registration — RFC 7591

RFC 7591 — OAuth 2.0 Dynamic Client Registration defines a JSON API for registering OAuth clients at runtime instead of through the admin portal. Paired with RFC 8414 — OAuth 2.0 Authorization Server Metadata, clients can discover endpoints and then self-register.

curl -X POST https://your-domain.com/orgs/{orgId}/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/orgs/{orgId}/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)RFC 7519 — JSON Web Token format; verifiable without a server call using the public keys at the JWKS endpoint
Refresh TokenOpaqueMust be exchanged at the token endpoint
ID TokenJWTOIDC identity claims (also a JWT per RFC 7519)
Authorization CodeOpaqueShort-lived, single-use

Token Revocation

RFC 7009 — OAuth 2.0 Token Revocation defines this endpoint. Clients call it to invalidate an access or refresh token immediately (for example on logout or when a token is compromised).

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