Log in with CAIRL (OpenID Connect)
Add CAIRL sign-in to your application through a standard OpenID Connect integration, with a Clerk walkthrough.
What this is
Add CAIRL sign-in through a compatible OpenID Connect integration. CAIRL sends a pseudonymous per-partner identifier and authentication information, not your name, email address, identity documents, age, or verification status. Verification is a separate, consented service.
Use this when your application already runs a login platform (targeting
Clerk's custom social connection first; Auth0, Okta, and similar OpenID
Connect clients are expected to work but are not promised until tested) and
you want "Sign in with CAIRL" without writing protocol code. If you want
verified claims such as age or identity, use the
OAuth and OIDC guide instead. The two purposes
never mix: a sign-in request cannot carry a verification scope, and a
verification request cannot carry openid.
Discovery
Point your login platform at the discovery document for the environment you integrate against. Every value it advertises is implemented; nothing else is.
| Environment | Discovery URL |
|---|---|
| Production | https://cairl.app/.well-known/openid-configuration |
| Staging | https://staging.cairl.app/.well-known/openid-configuration |
The issuer in each document equals that origin, and every ID token's iss
equals the issuer you discovered. Signing keys are published at
/.well-known/jwks.json on the same origin (RS256, rotated by publishing
the next key before it signs).
Register your application
An Owner or Admin of your CAIRL business context does two things in the dashboard. There is no self-service registration.
- Create a credential with Log in with CAIRL enabled on
/home/f/{slug}/keys. You receive aclient_idand a separately generatedclient_secret. The secret is shown once; store it only on your server (or in your login platform's secret field). - Register your callback URL on
/home/f/{slug}/connect, exactly as your login platform reports it. Matching is strict.
One host per login-enabled context. CAIRL issues a different sub for
each partner (pairwise subjects), so all redirect URIs registered for a
login-enabled context must share one host. A second host is refused at
registration. Use a separate business context for a second application.
The authorization request
Your platform redirects the User to the authorization endpoint (GET or POST) with:
| Parameter | Value |
|---|---|
response_type | code |
client_id | Your client ID |
redirect_uri | A registered callback URL |
scope | openid, alone |
state | Random value, returned unchanged |
code_challenge | PKCE S256 challenge (required) |
code_challenge_method | S256 |
nonce | Optional. When sent, it is echoed byte-for-byte in the ID token |
prompt | Optional. none, login, or consent |
max_age | Optional. Seconds since the User last authenticated to CAIRL |
The User signs in to CAIRL if needed (or again, when prompt=login or
max_age requires it), sees a consent screen naming your application and
stating exactly what is shared, and approves or denies. prompt=none never
shows a screen: it returns login_required or consent_required to your
callback instead.
The token exchange
Exchange the code at the token endpoint with one client authentication method:
client_secret_basic: anAuthorization: Basicheader, orclient_secret_post:client_idandclient_secretin the form body.
A request that uses both is rejected. The response contains:
{
"access_token": "…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid",
"id_token": "…"
}What the ID token contains
Exactly these claims, and never anything else:
| Claim | Meaning |
|---|---|
iss | The issuer you discovered |
sub | The User's pairwise identifier for your partner. Stable across sign-ins |
aud | Your client_id |
iat | Issued at |
exp | Five minutes after iat |
auth_time | When the User authenticated to CAIRL |
at_hash | Hash binding the ID token to the access token |
nonce | Only when you sent one |
amr | Only when known: pwd, otp, swk, or mfa |
The ID token never contains a name, email address, identity document, age, verification status, or any other CAIRL claim.
What userinfo returns
For a login token, GET or POST /api/oauth/userinfo with the bearer access
token returns exactly:
{ "sub": "pws_v1_…" }An ID token presented as a bearer token is rejected.
Errors
| Where | Code | Meaning |
|---|---|---|
| Authorization | invalid_scope | openid was combined with another scope, or the credential is not login-enabled |
| Authorization | invalid_request | Missing or unsupported PKCE, conflicting prompt, bad max_age |
| Authorization | login_required | prompt=none and the User is not signed in, or max_age is not met |
| Authorization | consent_required | prompt=none and consent has not been given |
| Authorization | access_denied | The User denied consent |
| Token | invalid_client | Wrong or missing client authentication |
| Token | invalid_request | Both client authentication methods in one request |
| Token | invalid_grant | Expired, replayed, or mismatched code, or the grant was revoked |
| Token | temporarily_unavailable | Signing is unavailable. Restart the flow |
Nothing is ever sent to an unregistered callback URL.
Revocation
A User can remove your application from their CAIRL connected-apps list at any time. Revocation blocks the next authorization, token exchange, and userinfo use for that User. It does not sign the User out of your application's own session, and an already-issued ID token remains valid until its five-minute expiry.
Clerk walkthrough (custom social connection)
Targeting Clerk's custom social connection. In your Clerk dashboard:
-
Open Configure → SSO connections, choose Add connection → For all users, and pick Custom OIDC provider.
-
Fill the fields:
Clerk field Value Name CAIRLKey cairlDiscovery endpoint The discovery URL for your environment (table above) Client ID Your CAIRL client ID Client Secret Your CAIRL client secret Scopes openid -
Copy the Callback URL Clerk shows for the connection and register it on
/home/f/{slug}/connect. -
Enable the connection. Clerk's hosted sign-in page now offers Continue with CAIRL.
Expected result: the User is sent to CAIRL, signs in, approves the consent
screen, and returns to your application signed in. Clerk stores the sub as
the external account identifier. Signing in again returns the same sub. Clerk
receives no name or email from CAIRL, so configure Clerk to collect any profile
fields your application needs.
Not included
Refresh tokens, profile and email scopes, logout, dynamic client
registration, sandbox OpenID Connect, and formal certification are not part of
this integration. Ask before building on any of them.