Complete reference of OAuth 2.0 and OpenID Connect standards implemented by AuthPI.
Last updated 2026-06-20
AuthPI implements OAuth 2.0 and OpenID Connect (OIDC) following the relevant RFCs and specifications. This document details which standards are supported, how they’re implemented, and any deviations or limitations.
| Standard | Status | Notes |
|---|---|---|
| OAuth 2.0 (RFC 6749) | Partial | Authorization code and refresh token grants |
| OAuth 2.0 Bearer Tokens (RFC 6750) | Full | |
| PKCE (RFC 7636) | Full | S256 only, required for public clients |
| Token Introspection (RFC 7662) | Full | |
| Token Revocation (RFC 7009) | Full | |
| JWT Access Tokens (RFC 9068) | Full | |
| Resource Indicators (RFC 8707) | Full | Audience parameter support |
| OpenID Connect Core 1.0 | Full | |
| OpenID Connect Discovery 1.0 | Full | |
| OpenID Connect Session Management 1.0 | Partial | Front-channel and back-channel logout |
Authorization Code Grant (RFC 6749 Section 4.1)
The primary grant type for web applications and native apps. The flow works as follows:
/authorize with response_type=code/tokenGET /authorize?
response_type=code&
client_id=cli_xxx&
redirect_uri=https://app.example.com/callback&
scope=openid%20profile%20email&
state=abc123
Refresh Token Grant (RFC 6749 Section 6)
Exchange a refresh token for new access and refresh tokens:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=eyJ...&
client_id=cli_xxx&
scope=openid profile
The optional scope parameter may narrow the granted scope for the issued access token (RFC 6749 §6) — it must be a subset of the original grant, or the request fails with invalid_scope. Omit it to receive the full grant. The token response includes the effective scope, and the rotated refresh token always retains the complete original grant, so narrowing one refresh does not shrink later ones.
AuthPI implements refresh token rotation—each refresh returns a new refresh token, and the old one is invalidated. This improves security by limiting the window for token theft. Granted scopes and organization memberships are re-applied on every refresh: scopes from the grant of record, memberships fresh from the directory.
If the original authorization request used org=org_..., refresh keeps that same selected-organization restriction. The refresh request itself cannot add or change org.
Client Credentials Grant (RFC 6749 Section 4.4)
Machine-to-machine authentication with no user involved:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
scope=read:data
The client authenticates via HTTP Basic (client_secret_basic) or client_id + client_secret form parameters (client_secret_post).
application_type: "m2m", confidential, with client_credentials in their grant_types) and to agents (agt_... identities authenticating with a secret verifier).openid scope is ignored for this grant.scope is requested, the token carries the client’s (or agent’s) full allowed scope set. Requesting a scope outside the allowed set fails with invalid_scope.resource parameter sets the token’s aud claim; otherwise the audience defaults to the client id.default_access_token_age (default 30 minutes). Agent tokens are fixed at 5 minutes so scope changes propagate quickly.Note: the client credentials grant issues OAuth access tokens for calling your own APIs. To call the AuthPI Core (admin) API from a backend, use org API keys instead.
The following grant types are not implemented:
Proof Key for Code Exchange prevents authorization code interception attacks. AuthPI fully implements PKCE with the following behavior:
| Client Type | PKCE Requirement |
|---|---|
| Public clients (SPAs, native apps) | Required |
| Confidential clients | Optional but recommended |
Only S256 is supported. The plain method is rejected for security reasons.
// Generate code verifier (43-128 characters, URL-safe)
const verifier = generateRandomString(64);
// Generate code challenge
const challenge = base64url(sha256(verifier));
// Authorization request
GET /authorize?
...&
code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
code_challenge_method=S256
// Token request includes verifier
POST /token
...&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
| Scenario | Error |
|---|---|
Public client without code_challenge | invalid_request |
code_challenge_method=plain | invalid_request |
Invalid code_verifier at token exchange | invalid_grant |
Missing code_verifier at token exchange | invalid_grant |
The introspection endpoint allows resource servers to validate tokens and retrieve their metadata.
Endpoint: POST /{issuer_id}/introspect
POST /introspect
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
token=eyJ...&
token_type_hint=access_token
The token_type_hint parameter is optional but helps AuthPI validate the token more efficiently. Accepted values: access_token, refresh_token.
{
"active": true,
"scope": "openid profile email",
"client_id": "cli_xxx",
"token_type": "Bearer",
"sub": "usr_xxx",
"aud": "cli_xxx",
"iat": 1705330953,
"exp": 1705332753,
"iss": "https://idp.authpi.com/iss_xxx",
"jti": "tok_xxx",
"sid": "ses_xxx",
"username": "user@example.com"
}
{
"active": false
}
A token is inactive if it’s expired, revoked, malformed, or issued by a different issuer.
Access tokens are validated by checking:
Refresh tokens are validated by checking:
sid)Revoke tokens when users log out or when tokens may be compromised.
Endpoint: POST /{issuer_id}/revoke
POST /revoke
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
token=eyJ...&
token_type_hint=refresh_token
The endpoint always returns 200 OK regardless of whether the token was valid or already revoked. This prevents information leakage about token validity.
Refresh tokens: Revoked via the session manager. The token’s JTI is marked as revoked, and subsequent uses of the refresh token fail.
Access tokens: Added to a revocation list with a TTL matching the token’s remaining lifetime. Resource servers should introspect tokens to check revocation status.
AuthPI issues access tokens as JWTs following RFC 9068.
{
"alg": "ES256",
"typ": "at+jwt",
"kid": "key_xxx"
}
{
"iss": "https://idp.authpi.com/iss_xxx",
"sub": "usr_xxx",
"aud": "cli_xxx",
"exp": 1705332753,
"iat": 1705330953,
"nbf": 1705330953,
"jti": "tok_xxx",
"client_id": "cli_xxx",
"sid": "ses_xxx",
"scope": "openid profile email",
"auth_time": 1705330900
}
| Algorithm | Description |
|---|---|
| ES256 | ECDSA with P-256 curve (default) |
| RS256 | RSA with SHA-256 |
| EdDSA | Edwards-curve DSA |
The algorithm is configurable per client via settings.openid.response_signature_alg.
AuthPI supports the audience parameter to request tokens for specific resource servers.
GET /authorize?
...&
audience=https://api.example.com
The requested audience must be either:
client_idallowed_audiences configurationWhen a custom audience is requested:
aud set to the requested audienceaud set to the client_idAuthPI supports an optional org parameter on the authorization request to issue tokens restricted to one organization:
GET /authorize?
...&
org=org_0gw3hcq8r2kfn7xj9tzm4be5a
The selected organization is stored with the authorization code and resulting session. /token does not accept org; token exchange and refresh use the trusted value from the authorization flow.
Validation happens before tokens are issued:
settings.restrictions.organizations.policy: "none", selected-org requests fail with invalid_request.policy: "allowlist", the requested org must appear in allowed_org_ids.invalid_grant.AuthPI is a fully compliant OpenID Connect Provider (OP).
ID tokens are JWTs containing identity claims about the authenticated user.
Required claims:
| Claim | Description |
|---|---|
iss | Issuer identifier URL |
sub | Subject identifier (user ID) |
aud | Audience (client ID) |
exp | Expiration time |
iat | Issued at time |
Authentication claims:
| Claim | Description |
|---|---|
auth_time | Time of authentication |
nonce | Client-provided nonce (if sent in request) |
acr | Authentication context class reference |
amr | Authentication methods used |
at_hash | Access token hash |
Profile claims (when requested via scopes):
| Scope | Claims |
|---|---|
profile | name, given_name, family_name, picture, locale |
email | email, email_verified |
phone | phone_number, phone_number_verified |
address | address |
Endpoint: GET|POST /{issuer_id}/userinfo
Returns claims about the authenticated user. Requires a valid access token with the openid scope.
GET /userinfo
Authorization: Bearer eyJ...
Response:
{
"sub": "usr_xxx",
"name": "Jane Doe",
"email": "jane@example.com",
"email_verified": true,
"picture": "https://example.com/photo.jpg"
}
| Scope | Description |
|---|---|
openid | Required for OIDC flows; triggers ID token issuance |
profile | Basic profile information |
email | Email address and verification status |
phone | Phone number and verification status |
address | Physical address |
The nonce parameter prevents replay attacks. When included in the authorization request, it’s embedded in the ID token for client verification.
GET /authorize?
...&
nonce=n-0S6_WzA2Mj
The client must verify that the nonce claim in the ID token matches the value sent in the request.
ACR (Authentication Context Class Reference):
AuthPI returns ACR values indicating the authentication assurance level:
urn:authpi:assurance:loa1 - Standard authenticationurn:authpi:method:{method} - Method-specific (e.g., urn:authpi:method:passkey)AMR (Authentication Methods References):
The amr claim contains an array of authentication methods used:
pwd - Passwordotp - One-time passwordmfa - Multi-factor authenticationhwk - Hardware key (passkey)AuthPI publishes its configuration at the standard discovery endpoint.
Endpoint: GET /{issuer_id}/.well-known/openid-configuration
{
"issuer": "https://idp.authpi.com/iss_xxx",
"authorization_endpoint": "https://idp.authpi.com/iss_xxx/authorize",
"token_endpoint": "https://idp.authpi.com/iss_xxx/token",
"userinfo_endpoint": "https://idp.authpi.com/iss_xxx/userinfo",
"jwks_uri": "https://idp.authpi.com/iss_xxx/jwks.json",
"introspection_endpoint": "https://idp.authpi.com/iss_xxx/introspect",
"revocation_endpoint": "https://idp.authpi.com/iss_xxx/revoke",
"end_session_endpoint": "https://idp.authpi.com/iss_xxx/logout",
"scopes_supported": ["openid", "email", "profile", "address", "phone"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["ES256", "RS256", "EdDSA"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"code_challenge_methods_supported": ["S256"],
"claims_supported": [
"sub", "iss", "aud", "exp", "iat",
"name", "given_name", "family_name",
"email", "email_verified",
"picture", "locale",
"acr", "amr", "auth_time"
],
"frontchannel_logout_supported": false,
"frontchannel_logout_session_supported": false,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true
}
Endpoint: GET /{issuer_id}/jwks.json
Returns the JSON Web Key Set containing public keys for verifying tokens.
{
"keys": [
{
"kty": "EC",
"use": "sig",
"kid": "key_xxx",
"alg": "ES256",
"crv": "P-256",
"x": "...",
"y": "..."
}
]
}
Keys are cached for 1 hour. Clients should cache JWKS responses and refresh periodically.
AuthPI supports OIDC Session Management for detecting session changes.
Endpoint: GET /{issuer_id}/check-session.html
Clients can embed this iframe and use postMessage to check if the user’s session is still valid without making network requests.
Endpoint: GET /{issuer_id}/logout
GET /logout?
id_token_hint=eyJ...&
post_logout_redirect_uri=https://app.example.com/logged-out&
state=xyz
| Parameter | Required | Description |
|---|---|---|
id_token_hint | Recommended | ID token for session identification |
post_logout_redirect_uri | Optional | Where to redirect after logout |
state | Optional | CSRF protection, echoed back |
The post_logout_redirect_uri must be registered in the client’s configuration.
When AuthPI can identify the underlying OP session, RP-initiated logout terminates that SSO anchor and the linked RP/client sessions created from it. Each affected client session can receive a back-channel logout notification when the client is configured for it.
AuthPI does not currently advertise or send OIDC front-channel logout iframe notifications. Use back-channel logout for client session notifications.
AuthPI can send logout tokens directly to relying parties’ back-channel logout endpoints. This is the supported logout notification mechanism and does not depend on the user’s browser.
| Method | Description |
|---|---|
client_secret_basic | Client ID and secret in HTTP Basic header |
client_secret_post | Client ID and secret in POST body |
none | No authentication (public clients only) |
Basic Authentication:
POST /token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=xxx&
redirect_uri=https://app.example.com/callback
POST Body Authentication:
POST /token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=xxx&
redirect_uri=https://app.example.com/callback&
client_id=cli_xxx&
client_secret=sec_xxx
private_key_jwt - JWT client assertion with private keyclient_secret_jwt - JWT client assertion with shared secrettls_client_auth - Mutual TLS authenticationDefault token lifetimes (configurable per client):
| Token Type | Default Lifetime |
|---|---|
| Authorization code | 10 minutes |
| Access token | 30 minutes |
| Refresh token | 7 days |
| ID token | Same as access token |
AuthPI returns OAuth 2.0 standard error responses:
{
"error": "invalid_grant",
"error_description": "The authorization code has expired"
}
Common error codes:
| Error | Description |
|---|---|
invalid_request | Missing or invalid parameter |
invalid_client | Client authentication failed |
invalid_grant | Authorization code or refresh token invalid |
unauthorized_client | Client not authorized for this grant type |
unsupported_grant_type | Grant type not supported |
invalid_scope | Requested scope is invalid or exceeds granted |
access_denied | User denied the authorization request |
response_type combinations like code id_token are not supportedrequest and request_uri parameters are not supportedprompt parameter is recognized but not fully enforcedmax_age parameter is not validatedui_locales parameter is not processedlogin_hint parameter is not usedS256nonce in ID tokens matches your requestaud claim matches your client IDstate parameter