Reference

OIDC & OAuth 2.0 Compliance

Complete reference of OAuth 2.0 and OpenID Connect standards implemented by AuthPI.

Last updated 2026-07-19

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)PartialAuthorization-code and refresh grants bind repeated resource values; client_credentials supports one resource
Client ID Metadata Documents (IETF draft)Opt-inPublic authorization-code clients; disabled by default per issuer
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=c_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=c_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

Client ID Metadata Documents

Client ID Metadata Documents (CIMD) let a public OAuth client use the exact HTTPS URL of a JSON metadata document as its client_id. AuthPI fetches and validates that document instead of looking up a stored Client resource. CIMD does not dynamically register the client or create a Client resource; see Registered Clients, CIMD, and Dynamic Registration for when to use each model.

CIMD is disabled by default. Enable it for an issuer through its settings:

{
  "settings": {
    "security": {
      "openid_policy": {
        "client_id_metadata_document": {
          "enabled": true,
          "organization_policy": "none"
        }
      }
    }
  }
}

organization_policy defaults to none. Set it to selected to allow only an explicitly selected active organization membership, or all to apply the issuer’s existing all-memberships behavior. When CIMD is disabled, URL-shaped client IDs are rejected.

Client ID URL requirements

The client_id URL must:

  • Use HTTPS and be no more than 1,000 characters.
  • Have a non-root path.
  • Have no username, password, fragment, IP-literal or local hostname, or ./.. path segment.

Ports and query strings are allowed. They are identity-significant: AuthPI uses the exact URL string for document identity, caching, and consent, without normalizing it.

Supported metadata

AuthPI reads this metadata subset. Additional non-secret properties, such as client_uri and logo_uri, are accepted but ignored:

FieldRequirement
client_idRequired. Must exactly equal the URL used in the authorization request.
client_nameRequired, non-empty, at most 200 characters.
redirect_urisRequired, with 1–100 entries. Every entry must satisfy the redirect policy below.
token_endpoint_auth_methodRequired and must be none. CIMD clients cannot use a client secret.
grant_typesOptional; defaults to ["authorization_code"]. May also include refresh_token.
response_typesOptional; defaults to ["code"]. No other response type is accepted.
scopeOptional space-separated scope list, at most 1,000 characters. It narrows the scopes the client may request.
id_token_signed_response_algOptional; EdDSA, ES256, or RS256.

CIMD clients are always public authorization-code clients and must use S256 PKCE. Secret-bearing metadata, including client_secret, client_secret_expires_at, and registration_access_token, is rejected.

{
  "client_id": "https://mcp-client.example.com/oauth/client-metadata.json",
  "client_name": "Example MCP client",
  "redirect_uris": [
    "https://mcp-client.example.com/oauth/callback",
    "http://127.0.0.1:6274/callback"
  ],
  "token_endpoint_auth_method": "none",
  "scope": "openid profile"
}

This example uses the omitted-field defaults for authorization code and code. To allow refresh tokens, add "grant_types": ["authorization_code", "refresh_token"]. The offline_access scope is valid only when refresh-token support is effective for both the document and issuer.

Every redirect URI in the document must be either:

  • An absolute HTTPS URL, or
  • An HTTP URL whose hostname is exactly localhost, 127.0.0.1, or [::1].

Credentials, fragments, malformed URLs, other HTTP hosts, and custom schemes such as javascript: or native-app schemes are rejected. The redirect_uri in the authorization request must exactly match one of the validated document values, including its scheme, host, port, path, and query.

Fetching and caching

AuthPI fetches metadata only after validating the user’s AuthPI session, and connections are restricted to public network addresses. The fetch uses a five-second timeout, does not follow redirects, requires HTTP 200 with application/json or application/*+json, and limits the response body to 5 KiB. AuthPI does not fetch or render URLs referenced by metadata fields such as logos, policies, terms, or JWKS.

Validated documents are cached according to shared-cache Cache-Control semantics, for at most one hour. s-maxage takes precedence over max-age; without explicit freshness, the cache lifetime is five minutes. no-store, no-cache, private, zero freshness, and effective lifetimes below 60 seconds prevent caching; failed or invalid responses are never cached.

Authorization display

CIMD authorization always presents a confirmation screen showing the client name, the client-ID hostname, and the requested redirect hostname, including a non-default port. AuthPI warns when the selected redirect uses a loopback address and displays a stronger warning when every redirect in the metadata document is loopback-only. Treat a localhost callback as belonging to whichever local process is listening on that port, not as proof of the client’s identity.

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": "c_xxx",
  "token_type": "Bearer",
  "sub": "usr_xxx",
  "aud": "c_xxx",
  "iat": 1705330953,
  "exp": 1705332753,
  "iss": "https://idp.authpi.com/i_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/i_xxx",
  "sub": "usr_xxx",
  "aud": "c_xxx",
  "exp": 1705332753,
  "iat": 1705330953,
  "nbf": 1705330953,
  "jti": "tok_xxx",
  "client_id": "c_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)

The OAuth resource parameter binds an authorization grant and its access tokens to the protected resource that will consume them. For authorization-code and refresh grants, send one form parameter per resource; do not combine several resource identifiers into a comma- or space-separated value.

GET /{issuer_id}/authorize?response_type=code
  &client_id=c_xxx
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=read%3Areports
  &state=abc123
  &resource=https%3A%2F%2Fapi.example.com%2Freports
  &resource=https%3A%2F%2Fapi.example.com%2Fexports

Repeat the resources at authorization-code exchange to select the full authorized set or a subset:

POST /{issuer_id}/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=...&
client_id=c_xxx&
redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
code_verifier=...&
resource=https%3A%2F%2Fapi.example.com%2Freports&
resource=https%3A%2F%2Fapi.example.com%2Fexports

The token request cannot introduce a resource that was absent from the authorization request. When resource indicators are optional and the code exchange omits resource, AuthPI selects the full set authorized by the original request.

A refresh request may select any subset of the immutable resource ceiling established by the original authorization request, including resources that were not selected at code exchange. Under an optional policy, omitting resource retains the session’s most recently selected set. A required policy rejects omission. Refresh rotation updates only that current selection; it never changes the original ceiling, so a later refresh may choose a different subset but can never add a new resource.

POST /{issuer_id}/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=...&
client_id=c_xxx&
resource=https%3A%2F%2Fapi.example.com%2Freports

One selected resource produces a string aud claim. Multiple selected resources produce an aud array. Array order has no meaning; resource servers must check membership rather than position. ID tokens and refresh tokens continue to use the client ID as their audience. See Token Claims for the complete claim rules.

This layered RFC 8707 policy applies to authorization-code and refresh-token flows. The client_credentials grant keeps its existing behavior: it accepts at most one resource string as the access-token audience, does not consult resource_indicators or allowed_resources, and reports malformed values as invalid_request rather than invalid_target.

Resource identifier rules

Each resource identifier must be a non-empty RFC 3986 absolute URI of at most 1,000 characters. It must use ASCII URI characters and valid percent escapes, with no whitespace, control characters, backslashes, or fragment, including an empty trailing #. One request may contain at most ten resource values. AuthPI preserves and compares the exact string—it does not normalize scheme or host casing, add or remove a trailing slash, decode percent escapes, or discard a query string. For example, these are different resources:

  • https://api.example.com/reports
  • https://API.example.com/reports
  • https://api.example.com/reports/
  • https://api.example.com/reports?region=eu

Exact duplicate values collapse to one entry. Distinct values form a set for authorization checks, so request order is not authorization-significant and consumers must not depend on audience-array order.

Issuer and client policy

Resource authorization is layered. The issuer defines the available resources and the scopes valid for each one under settings.security.openid_policy.resource_indicators:

{
  "settings": {
    "security": {
      "openid_policy": {
        "resource_indicators": {
          "required": false,
          "allow_multiple": true,
          "resources": [
            {
              "resource": "https://api.example.com/reports",
              "scopes": ["read:reports"]
            },
            {
              "resource": "https://api.example.com/exports",
              "scopes": ["read:reports"]
            }
          ]
        }
      }
    }
  }
}

resources accepts at most 100 unique entries, with at most 100 scopes on each entry. required defaults to false; when true, the authorization request and every authorization-code or refresh-token request must explicitly select a resource. Omission never falls back to the grant or session selection while this flag is enabled. allow_multiple defaults to true; when false, a request may select only one distinct resource. Every effective scope must appear in the scopes list of every selected resource. A scope that is valid for only one resource cannot be put into a token intended for several resources.

Enable required only after every affected client sends resource on authorization, code exchange, and refresh. Turning it on intentionally makes existing resource-less authorization families fail their next token request with invalid_target; it does not infer a resource from client configuration or a legacy audience.

Registered clients must also opt in to each resource under settings.openid.allowed_resources, which accepts at most 100 exact URIs:

{
  "settings": {
    "openid": {
      "allowed_resources": [
        "https://api.example.com/reports",
        "https://api.example.com/exports"
      ]
    }
  }
}

The effective set for a registered client is the intersection of the issuer catalog and allowed_resources. An omitted or empty allowed_resources authorizes no RFC 8707 resource; it does not inherit the issuer catalog and does not implicitly authorize the client ID. A Client ID Metadata Document has no registered-client settings, so CIMD clients use the issuer catalog as their resource allowlist; the document’s own scope restrictions still apply.

Prior consent skips the consent screen only when the requested scopes and resources are both subsets of the same stored grant. Newly requested resources appear as read-only exact URIs on the consent screen; the browser can choose scopes but cannot add or replace resource values. A new consent decision replaces the previous scope-and-resource grant as one unit rather than unioning the two lists independently.

Errors

In authorization-code and refresh-token flows, malformed, missing-but-required, unknown, disallowed, excessive, or scope-incompatible resource selections fail with invalid_target. At the authorization endpoint, AuthPI redirects this error only after it has established that the supplied redirect_uri belongs to the client; the redirect includes error=invalid_target and the original state. If the client or redirect URI cannot be trusted—including an unresolved or invalid CIMD redirect—AuthPI returns a direct 400 JSON response and never redirects to the supplied URI.

At the token endpoint, invalid_target is always a direct 400 JSON response. Error descriptions do not enumerate the issuer’s resource catalog or the client’s allowlist.

Resource-rejection diagnostics classify failures as missing_required, malformed, too_many, multiple_disallowed, ambiguous_legacy, not_allowed, not_in_grant, scope_mismatch, policy_changed, or unknown. Diagnostic context is limited to the grant type, client kind, and resource count; requested resource URIs and configured allowlists are excluded.

The legacy audience parameter remains available for existing integrations and continues to use the registered client’s allowed_audiences. A request must not combine audience and resource; AuthPI rejects that ambiguity with invalid_request.

To migrate from audience:

  1. Add each protected-resource URI and its valid scopes to the issuer’s resource_indicators.resources.
  2. Add the same exact URI to each registered client’s allowed_resources.
  3. Send resource at both authorization and code exchange, then on refresh when selecting a subset.
  4. Update resource-server verification to accept aud as either a string or an array containing its identifier.
  5. Stop sending audience; never send both parameters during a staged rollout.

Enable required resource selection

Start with required: false. Configure the issuer resource catalog and each registered client’s allowed_resources, then update every affected client to send exact configured resource values during authorization and permitted subsets during code exchange or refresh. Verify those requests before changing the issuer policy to required: true.

After required is enabled, existing authorization families that were created without a resource fail their next code-exchange or refresh request with invalid_target. Start a new authorization flow for those sessions. During a staged migration, send either audience or resource, never both.

MCP resource profile

The MCP authorization specification requires the MCP server’s canonical resource URI at authorization and token exchange. For an MCP server at https://mcp.authpi.com/mcp, configure one required resource and disable multiple-resource requests:

{
  "settings": {
    "security": {
      "openid_policy": {
        "resource_indicators": {
          "required": true,
          "allow_multiple": false,
          "resources": [
            {
              "resource": "https://mcp.authpi.com/mcp",
              "scopes": ["openid", "offline_access", "issuers:read"]
            }
          ]
        }
      }
    }
  }
}

Registered clients must also include the exact string https://mcp.authpi.com/mcp in settings.openid.allowed_resources; CIMD clients are governed by the issuer entry. Variants such as https://mcp.authpi.com, https://mcp.authpi.com/mcp/, or a differently cased hostname do not match. Clients must send the exact configured value at authorization, code exchange, and refresh.

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/i_xxx",
  "authorization_endpoint": "https://idp.authpi.com/i_xxx/authorize",
  "token_endpoint": "https://idp.authpi.com/i_xxx/token",
  "userinfo_endpoint": "https://idp.authpi.com/i_xxx/userinfo",
  "jwks_uri": "https://idp.authpi.com/i_xxx/jwks.json",
  "introspection_endpoint": "https://idp.authpi.com/i_xxx/introspect",
  "revocation_endpoint": "https://idp.authpi.com/i_xxx/revoke",
  "end_session_endpoint": "https://idp.authpi.com/i_xxx/logout",

  "scopes_supported": ["openid", "email", "profile", "address", "phone"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
  "subject_types_supported": ["public"],

  "id_token_signing_alg_values_supported": ["ES256", "RS256", "EdDSA"],
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "none"],
  "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
}

When CIMD is enabled for the issuer, discovery also includes "client_id_metadata_document_supported": true. The property is omitted while CIMD is disabled.

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=c_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