OAuth and OIDC Guide
Map CAIRL hosted verification into OAuth authorization and OIDC-style claim retrieval.
Overview
CAIRL's hosted verification flow uses OAuth 2.0 Authorization Code with PKCE. Your application redirects the user to CAIRL, receives an authorization code, exchanges the code server-to-server, and reads verified claims from the userinfo endpoint.
Use this guide when your application already has an OAuth or OIDC integration layer and you want to plug CAIRL into that layer without collecting identity documents yourself.
If you only want sign-in (no verified claims), use Log in with CAIRL instead: a standard OpenID Connect login with discovery and an ID token. The two purposes never mix in one request.
Endpoints
| Endpoint | Purpose |
|---|---|
GET https://cairl.app/verify/start | Hosted verification entry point; a fresh verification for your site, then consent |
GET /oauth/authorize (same host) | Reusable-verification path: with verification scopes it honours an existing verification that meets the key's depth and falls inside its time-based freshness window, then goes to consent. Same parameters as /verify/start plus the required response_type=code. This is not the sign-in flow — for that use the OIDC flow (Log in with CAIRL): the openid scope alone, a login-enabled credential, and an ID token. |
POST https://cairl.app/api/oauth/token | Token exchange (grant_type=authorization_code only) |
GET https://cairl.app/api/oauth/userinfo | Verified claims snapshot |
POST https://cairl.app/api/oauth/revoke | Token revocation (RFC 7009) |
GET https://cairl.app/.well-known/jwks.json | Public signing keys for the access-token JWT |
GET https://cairl.app/.well-known/openid-configuration | OpenID Connect discovery (used by Log in with CAIRL) |
The freshness half of that check covers the time-based policies only —
daily, 3day, weekly, biweekly, monthly. The event-driven policies,
session and transaction, are not enforced at /oauth/authorize today.
What is not published for verification requests:
- No refresh tokens. Run the authorization flow again for a new snapshot.
- No ID token on a verification request. Read claims from userinfo (or from
the access-token JWT's
cairl_claims, verified against the JWKS). An ID token is issued only for a sign-in request, which carries theopenidscope alone and requires a login-enabled credential.
Authorization endpoint
Redirect users to:
https://cairl.app/verify/startRequired parameters:
| Parameter | Description |
|---|---|
client_id | Your CAIRL test or live client identifier |
redirect_uri | Exact callback URL registered on /home/f/{slug}/connect |
state | Random CSRF token, at least 16 characters, returned unchanged |
scope | Space-delimited wire scopes such as age:18+ identity:verified |
code_challenge | PKCE S256 challenge |
code_challenge_method | Must be S256 |
PKCE is mandatory; a request without code_challenge is rejected.
The scope values are wire scopes (age:18+), not the claim names that come
back in the response (age_18_plus). The full list is in the
claims reference.
See the Quickstart for complete Node and Python PKCE examples.
Token endpoint
Exchange the authorization code at:
POST https://cairl.app/api/oauth/tokenThe token request uses application/x-www-form-urlencoded (JSON is also
accepted) and requires all of:
| Field | Description |
|---|---|
grant_type | authorization_code |
code | Code from the callback |
client_id | Your CAIRL test or live client identifier |
client_secret | Server-side secret from /home/f/{slug}/keys |
redirect_uri | Same callback URL used in the authorization request |
code_verifier | Original PKCE verifier |
The response includes a bearer access_token, token_type, expires_in, and
the granted scope. With live credentials, an insufficient balance returns
402 payment_required and no token.
Userinfo and claim retrieval
Read verified claims at:
GET https://cairl.app/api/oauth/userinfo
Authorization: Bearer ACCESS_TOKENThe response is OIDC-style: it includes a site-scoped sub plus the verified
claims the user authorized, and a mode field ("live" or "test"). In
live mode sub is stable for that user on your site and is the identifier
to match accounts on. In test mode (a cairl_test_… credential) every
exchange returns a fresh, random pws_v1_test_… subject and synthetic claims,
so never build account matching against a test-mode sub; branch on mode
instead. CAIRL does not return raw document images or default raw identity
fields through this endpoint.
{
"sub": "pws_v1_abc123",
"evaluated_at": "2026-05-03T10:00:00.000Z",
"claims": {
"age_18_plus": true,
"identity_verified": true
},
"meta": {
"claims_requested": ["age_18_plus", "identity_verified"],
"claims_resolved": ["age_18_plus", "identity_verified"],
"claims_null": [],
"claims_ignored": []
}
}OIDC compatibility notes
- Treat
subas a pseudonymous subject identifier scoped to your CAIRL client. - Use scopes to request only the claims your product needs.
- Validate
statebefore exchanging the authorization code. - Store and use the
client_secretonly on your server. - Do not expect an ID token on a verification request; use userinfo as the canonical claims response. For sign-in with an ID token, see Log in with CAIRL.
- Your library may read
/.well-known/openid-configurationfor the endpoint URLs and JWKS location; verification scopes are listed there besideopenid.