Reference

OIDC & OAuth 2.0 Compliance

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.

Standards Overview

StandardStatusNotes
OAuth 2.0 (RFC 6749)PartialAuthorization code and refresh token grants
OAuth 2.0 Bearer Tokens (RFC 6750)Full
PKCE (RFC 7636)FullS256 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)FullAudience parameter support
OpenID Connect Core 1.0Full
OpenID Connect Discovery 1.0Full
OpenID Connect Session Management 1.0PartialFront-channel and back-channel logout

OAuth 2.0 (RFC 6749)

Supported Grant Types

Authorization Code Grant (RFC 6749 Section 4.1)

The primary grant type for web applications and native apps. The flow works as follows:

  1. Client redirects user to /authorize with response_type=code
  2. User authenticates and consents
  3. AuthPI redirects back with an authorization code
  4. Client exchanges code for tokens at /token
GET /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).

  • Available to M2M clients (application_type: "m2m", confidential, with client_credentials in their grant_types) and to agents (agt_... identities authenticating with a secret verifier).
  • The response contains an access token only — no refresh token and no ID token. The openid scope is ignored for this grant.
  • If no 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.
  • The optional resource parameter sets the token’s aud claim; otherwise the audience defaults to the client id.
  • Token lifetime: the client’s 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.

Not Supported

The following grant types are not implemented:

  • Implicit Grant (RFC 6749 Section 4.2): Deprecated; use authorization code with PKCE
  • Resource Owner Password (RFC 6749 Section 4.3): Not recommended due to security concerns
  • Device Authorization (RFC 8628): Not currently supported

PKCE (RFC 7636)

Proof Key for Code Exchange prevents authorization code interception attacks. AuthPI fully implements PKCE with the following behavior:

Requirements

Client TypePKCE Requirement
Public clients (SPAs, native apps)Required
Confidential clientsOptional but recommended

Supported Methods

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

Error Handling

ScenarioError
Public client without code_challengeinvalid_request
code_challenge_method=plaininvalid_request
Invalid code_verifier at token exchangeinvalid_grant
Missing code_verifier at token exchangeinvalid_grant

Token Introspection (RFC 7662)

The introspection endpoint allows resource servers to validate tokens and retrieve their metadata.

Endpoint: POST /{issuer_id}/introspect

Request

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.

Response (Active 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"
}

Response (Inactive Token)

{
  "active": false
}

A token is inactive if it’s expired, revoked, malformed, or issued by a different issuer.

Access vs Refresh Token Introspection

Access tokens are validated by checking:

  1. JWT signature validity
  2. Expiration time
  3. Revocation status in the revocation list

Refresh tokens are validated by checking:

  1. JWT signature validity
  2. Session binding (must have valid sid)
  3. Token rotation (JTI must match current token)

Token Revocation (RFC 7009)

Revoke tokens when users log out or when tokens may be compromised.

Endpoint: POST /{issuer_id}/revoke

Request

POST /revoke
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

token=eyJ...&
token_type_hint=refresh_token

Response

The endpoint always returns 200 OK regardless of whether the token was valid or already revoked. This prevents information leakage about token validity.

Revocation Behavior

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.

JWT Access Tokens (RFC 9068)

AuthPI issues access tokens as JWTs following RFC 9068.

{
  "alg": "ES256",
  "typ": "at+jwt",
  "kid": "key_xxx"
}

Payload

{
  "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
}

Supported Signing Algorithms

AlgorithmDescription
ES256ECDSA with P-256 curve (default)
RS256RSA with SHA-256
EdDSAEdwards-curve DSA

The algorithm is configurable per client via settings.openid.response_signature_alg.

Resource Indicators (RFC 8707)

AuthPI supports the audience parameter to request tokens for specific resource servers.

Request

GET /authorize?
  ...&
  audience=https://api.example.com

Validation

The requested audience must be either:

  • The client’s own client_id
  • Listed in the client’s allowed_audiences configuration

Token Audience

When a custom audience is requested:

  • Access tokens have aud set to the requested audience
  • Refresh tokens always have aud set to the client_id

Selected Organizations

AuthPI 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:

  • If the client has settings.restrictions.organizations.policy: "none", selected-org requests fail with invalid_request.
  • If the client has policy: "allowlist", the requested org must appear in allowed_org_ids.
  • If the user is not an active member of the selected org, the authorization-code exchange fails with invalid_grant.

OpenID Connect Core 1.0

AuthPI is a fully compliant OpenID Connect Provider (OP).

ID Token

ID tokens are JWTs containing identity claims about the authenticated user.

Required claims:

ClaimDescription
issIssuer identifier URL
subSubject identifier (user ID)
audAudience (client ID)
expExpiration time
iatIssued at time

Authentication claims:

ClaimDescription
auth_timeTime of authentication
nonceClient-provided nonce (if sent in request)
acrAuthentication context class reference
amrAuthentication methods used
at_hashAccess token hash

Profile claims (when requested via scopes):

ScopeClaims
profilename, given_name, family_name, picture, locale
emailemail, email_verified
phonephone_number, phone_number_verified
addressaddress

UserInfo Endpoint

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"
}

Scopes

ScopeDescription
openidRequired for OIDC flows; triggers ID token issuance
profileBasic profile information
emailEmail address and verification status
phonePhone number and verification status
addressPhysical address

Nonce

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.

Authentication Context

ACR (Authentication Context Class Reference):

AuthPI returns ACR values indicating the authentication assurance level:

  • urn:authpi:assurance:loa1 - Standard authentication
  • urn: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 - Password
  • otp - One-time password
  • mfa - Multi-factor authentication
  • hwk - Hardware key (passkey)

OpenID Connect Discovery 1.0

AuthPI publishes its configuration at the standard discovery endpoint.

Endpoint: GET /{issuer_id}/.well-known/openid-configuration

Response

{
  "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
}

JWKS Endpoint

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.

Session Management

AuthPI supports OIDC Session Management for detecting session changes.

Check Session Iframe

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.

RP-Initiated Logout

Endpoint: GET /{issuer_id}/logout

GET /logout?
  id_token_hint=eyJ...&
  post_logout_redirect_uri=https://app.example.com/logged-out&
  state=xyz
ParameterRequiredDescription
id_token_hintRecommendedID token for session identification
post_logout_redirect_uriOptionalWhere to redirect after logout
stateOptionalCSRF 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.

Front-Channel Logout

AuthPI does not currently advertise or send OIDC front-channel logout iframe notifications. Use back-channel logout for client session notifications.

Back-Channel Logout

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.

Client Authentication

Supported Methods

MethodDescription
client_secret_basicClient ID and secret in HTTP Basic header
client_secret_postClient ID and secret in POST body
noneNo 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

Not Supported

  • private_key_jwt - JWT client assertion with private key
  • client_secret_jwt - JWT client assertion with shared secret
  • tls_client_auth - Mutual TLS authentication

Token Lifetimes

Default token lifetimes (configurable per client):

Token TypeDefault Lifetime
Authorization code10 minutes
Access token30 minutes
Refresh token7 days
ID tokenSame as access token

Error Responses

AuthPI returns OAuth 2.0 standard error responses:

{
  "error": "invalid_grant",
  "error_description": "The authorization code has expired"
}

Common error codes:

ErrorDescription
invalid_requestMissing or invalid parameter
invalid_clientClient authentication failed
invalid_grantAuthorization code or refresh token invalid
unauthorized_clientClient not authorized for this grant type
unsupported_grant_typeGrant type not supported
invalid_scopeRequested scope is invalid or exceeds granted
access_deniedUser denied the authorization request

Limitations

Features Not Implemented

  • Hybrid flows: response_type combinations like code id_token are not supported
  • Request objects: The request and request_uri parameters are not supported
  • Pushed Authorization Requests (RFC 9126): Not implemented
  • DPoP (RFC 9449): Demonstration of Proof of Possession not supported
  • CIBA: Client-Initiated Backchannel Authentication not supported

Parameter Limitations

  • prompt parameter is recognized but not fully enforced
  • max_age parameter is not validated
  • ui_locales parameter is not processed
  • login_hint parameter is not used

Security Recommendations

For Confidential Clients

  1. Store client secrets securely (environment variables, secret managers)
  2. Use HTTPS for all redirect URIs
  3. Implement PKCE even though it’s optional
  4. Rotate client secrets periodically

For Public Clients

  1. PKCE is mandatory—always use S256
  2. Store tokens securely (avoid localStorage for sensitive data)
  3. Use short-lived access tokens
  4. Implement token refresh handling

For All Clients

  1. Validate ID token signatures using JWKS
  2. Verify nonce in ID tokens matches your request
  3. Check aud claim matches your client ID
  4. Implement proper CSRF protection with state parameter
  5. Use token introspection for high-security operations

Next Steps