Integration guides

Authentication

Authenticate users through the Authorization Code Flow or Device Authorization Grant. Your backend or existing auth server is the confidential client; browser-only, native, and CLI clients use that backend. Set up the client first.

Configure your OIDC library

SettingValue
Issuerhttps://auth.world.org
Discoveryhttps://auth.world.org/.well-known/openid-configuration
Scopeopenid, optionally with email
Client authenticationYour registered method; see Client authentication

Read endpoints and jwks_uri from discovery. Use the same environment for registration, credentials, and discovery. Request openid, optionally email; remove library defaults for profile, offline_access, prompt=consent, and prompt=select_account. For delegation with offline_access, follow XAA.

To accept people verified by either Orb or Selfie Check, send both authentication classes as described in Accept Orb or Selfie Check. Omitting them uses your client's accepted accounts.

Authorization Code Flow

Use OIDC Core's code flow (opens in a new tab) with response_type=code, query response mode, and S256 PKCE (opens in a new tab), including for confidential clients. Keep a fresh state, optional fresh nonce, and private PKCE verifier bound to each server-side attempt.

The decoded authorization parameters are:

GET https://auth.world.org/api/v1/authorize
client_id=<portal-client-id>
redirect_uri=https://app.example/auth/world/callback
response_type=code
scope=openid
state=<fresh-random-state>
nonce=<fresh-random-nonce>
code_challenge=<base64url-sha256-of-verifier-without-padding>
code_challenge_method=S256

World IdP handles passkey or proof authentication and browser-session eligibility. At the callback, validate state and require the response's iss to match the discovered issuer exactly before redeeming the code. Both success and error redirects include iss. Authorization errors end the attempt. Codes expire after five minutes and can be redeemed once.

For a client registered with client_secret_post, exchange the code with these decoded form fields:

POST https://auth.world.org/api/v1/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
client_id=<portal-client-id>
client_secret=<backend-secret>
code=<returned-code>
redirect_uri=https://app.example/auth/world/callback
code_verifier=<original-verifier>

URL-encode requests. For the portal default client_secret_basic or private_key_jwt, use the registered method. Validate the result before using its claims.

Device Authorization Grant

World IdP supports the Device Authorization Grant (RFC 8628) (opens in a new tab) for active, portal-registered confidential clients, regardless of their sector. Use discovery's device_authorization_endpoint and token_endpoint with your registered client-authentication method and scope=openid.

Run requests from your backend. The service running an agent or CLI can act as that backend when it securely stores credentials and validates the ID token. Keep credentials, device codes, and tokens in server-side storage, outside model messages. For delegation with openid offline_access, follow the XAA profile.

Supported device profile

The controls and freshness rules below apply to World ID authentication with openid.

Configure the client for the device grant separately from browser authorization:

Input or resultDevice grant contract
Initiationscope=openid, form-encoded, with confidential client authentication. For delegation, use the XAA profile.
PollingForm-encoded grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code, and the same client's authentication.
Browser parametersNo callback, PKCE, or state exchange. nonce, prompt, and max_age do not provide request controls for this grant. Fresh authentication is required regardless.
Authentication classSupports acr_values and essential ID-token acr requests through claims; see Face Auth.
Unknown parametersIgnored when supplied once. A successful initiation does not mean an extension was understood or enforced.
Malformed or repeated fieldsMalformed form encoding and repeated fields, including unknown parameters and identical repeated values, return invalid_request.
ID tokenValidate signature, issuer, audience, expiry, and required auth_time, acr, and amr. Device tokens omit nonce, even if one was sent; accept its absence for this profile.
binding_message or action termsUnsupported. They are not displayed on the approval page or signed into the ID token. The human sees the requester and user code.

The ID token provides authentication context. Your application handles any separate business-action display, consent, and authorization. If your policy requires a signed action-approval receipt, keep the operation blocked until you validate one through a profile that advertises support for it.

Device flow

  1. Start. POST scope=openid to discovery's device_authorization_endpoint (https://auth.world.org/api/v1/device_authorization) with your registered client authentication. Bind the attempt to the requesting client's private backend session.
  2. Hand off approval. Show the returned user_code and exact verification_uri_complete, or verification_uri with instructions to enter the code. Keep device_code private, outside UI output, logs, and model messages. expires_in and interval are in seconds.
  3. Poll. After at least interval, send the device-code grant to the token endpoint with the same client's authentication. Apply the response rules below. Approval allows redemption; denial ends the attempt.
  4. Validate and bind. Validate the ID token, accepting its nonce omission for this grant. Atomically bind (iss, sub) to the intended application session or grant before authorizing access. Apply your account, freshness, assurance, and permission checks before using the result.
Poll responseWhat to do
HTTP 400 authorization_pendingKeep waiting and poll again after the interval.
HTTP 400 slow_downAdd five seconds to all later intervals. An early poll can return this before the grant is checked; it never extends the attempt's expiry.
access_denied, expired_token, invalid_grant, other OAuth errorStop polling.
HTTP 503Temporary unavailability, never approval. End this attempt.
HTTP 429Per-network-address flood protection on back-channel endpoints, separate from the per-client limits. It can end the attempt; Retry-After does not override the stop rules.
Connection timeoutBack off before polling again.

Set each request's timeout within the attempt's remaining lifetime, and stop when the attempt expires. Do not automatically restart authorization in a loop after a failed attempt.

For a client registered with client_secret_post, the decoded request fields are:

POST https://auth.world.org/api/v1/device_authorization
Content-Type: application/x-www-form-urlencoded

client_id=<portal-client-id>
client_secret=<backend-secret>
scope=openid

After waiting the returned interval, the decoded poll form is:

POST https://auth.world.org/api/v1/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code
client_id=<same-portal-client-id>
client_secret=<backend-secret>
device_code=<private-device-code>

Use your form encoder. For other methods, use the registered client authentication.

Expiry and rate limits

  • The device code expires 20 minutes after creation, even if approval occurs near expiry, and can be redeemed once. See recovery after interrupted requests for when to retry or start again.
  • If you cannot tell whether a start request succeeded, it may leave an unused attempt until expiry. Do not retry it in a tight loop.
  • Starts and polls share a per-client request limit, and starts also have a per-client initiation limit. A limited start returns HTTP 429 with Retry-After: 60; a limited poll returns HTTP 400 slow_down, so polling can continue at the increased interval. Confirm that your backend or agent client supports the device flow explicitly.

Validate the ID token

Use an OIDC library for standard ID token validation (opens in a new tab), with issuer https://auth.world.org, your client ID as audience, and RS256 signing keys from discovery's jwks_uri. Use bounded JWKS refresh and handle key overlap. Validate a requested browser nonce; device tokens omit it. For required freshness and assurance, also enforce the step-up policy against auth_time, acr, and amr.

Use (iss, sub) for account matching. See Tokens and identity for token responses, claim values, UserInfo, and subject sharing.

Configure your auth server

If an auth server handles your application's sign-in, configure its OIDC connector with the settings above. Register the auth server's exact callback URL and use a client-authentication method it supports. It validates the ID token from World IdP, maps (iss, sub) to the application account, and issues its own sessions or resource-bound access tokens after checking permissions.

Configure the connector to handle accounts without email claims. Preserve the validated auth_time, acr, and amr when enforcing step-up. Several applications behind one auth server may share a World ID subject; your auth server controls how its downstream tenants and users are separated.

Refreshes under your auth server's policy preserve the original World ID authentication time. World IdP provides no advertised OIDC end-session or back-channel logout endpoint. For account linking, follow Store and match sub; for callback migrations, follow Manage credentials and callbacks.

Primary sign-in

To use World ID as your application's primary or only sign-in, validate a browser or device ID token, find or create the account by (iss, sub), and issue your own session. Handle accounts without email claims. Your application owns its sessions, permissions, sign-out, and revocation. Ending World IdP's browser session or revoking its saved consent leaves your application's sessions and tokens under its own policy.

Recovery after interrupted requests

  • Code exchange returns HTTP 503 or times out: You can retry a limited number of times before the code's original expiry, using the same code, redirect URI, and PKCE verifier.
  • Device poll fails: Follow the polling and stop rules.
  • Exchange returns invalid_grant: Treat it as failure. Start a new authentication attempt. Each code can be redeemed once. If a successful exchange's response was lost and you retained no tokens, you must also start again.

For private_key_jwt, generate a fresh assertion for every retry or poll, even after a signing failure. Keep codes, assertions, tokens, PKCE verifiers, and client credentials out of logs.

Limitations

  • Implicit and hybrid grants, client credentials, and request objects (request / request_uri) are unsupported. Refresh and token exchange belong to XAA.
  • The authentication access token authorizes UserInfo. Your backend issues credentials for its own APIs and MCP resources.
  • The device profile specifies supported controls and the authentication result's action-approval boundary.

Verify the integration

Follow Sandbox and testing, including accounts with and without email, account linking, wrong-account rejection, and step-up through existing sessions. Match UserInfo sub to the ID token if you use UserInfo.

Troubleshooting

OAuth failures include a human-readable error_description alongside error in the JSON response or authorization error redirect. Use the description to diagnose malformed parameters, and use the error code to decide how your application responds. Descriptions never echo submitted values. A client or callback mismatch returns an error without redirecting to your callback, using the same guidance for either mismatch. Credential, grant, and service errors use deliberately generic descriptions. Keep dependency failures distinguishable from invalid authentication.

SymptomCheck
invalid_scopeRequest openid for World ID authentication or openid offline_access for XAA delegation when discovery advertises it; email is also accepted. Remove other library scopes.
HTTP 400 unsupported_grant_typeUse a grant type advertised by discovery. See XAA availability for refresh and exchange.
Authorization invalid_requestCheck the active client, exact registered callback, state, nonce, S256 challenge, response mode, and supported prompt. Bad client/callback requests fail without redirecting to your callback.
invalid_client at token/device endpointCheck environment, registered auth method, credential encoding, and that methods are not mixed. For JWTs check audience, key, expiry, and a fresh JTI per request. Basic failures use HTTP 401; POST/JWT failures use HTTP 400.
invalid_grantCheck expiry, single-use code, exact callback, PKCE verifier, and client binding. Do not blindly replay a possibly consumed code.
login_required / interaction_requiredA prompt=none request cannot complete silently. Start an interactive request when appropriate.
account_not_eligible / client_registration_disabled in portalCheck participant Google-account eligibility and registration availability with the organizer/operator. These are not World ID proof failures.
HTTP 503Treat as temporary unavailability, never invalid identity or successful approval. Use bounded requests; apply the device polling rules above.