Integrate CAGE in minutes.

Standard OAuth 2.0 / OpenID Connect. If you've integrated "Sign in with Google", you already know how this works.

CAGE is open source — inspect the server code, verify the claims, or contribute on GitHub.

QUICK START

Four steps to age verification

CAGE implements standard OAuth 2.0 with OpenID Connect extensions. The flow is identical to any OAuth provider: redirect, callback, token exchange, claims read.

1. Register as a partner

Contact us at partners@cageid.app to get your credentials. Self-serve registration is coming soon.

You'll receive:

credentials
client_id:     a1b2c3d4-5678-9abc-def0-1234567890ab
client_secret: csk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keep your client_secret secure — treat it like a password. Never expose it in client-side code.

2. Redirect to CAGE

When a user needs to verify their age, redirect them to the CAGE authorization endpoint:

GET https://api.cageid.app/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yoursite.com/callback
  &response_type=code
  &state=RANDOM_STATE_STRING
  &scope=openid%20age_verification
client_idYour partner client ID (UUID) from registration
redirect_uriMust be pre-registered and match exactly
response_typeAlways code
stateRandom string — verify on callback to prevent CSRF
scopeopenid age_verification — recommended, but optional

3. Exchange the code for a token

After the user authenticates, CAGE redirects back to your redirect_uri with a short-lived auth code. Exchange it server-side:

POST https://api.cageid.app/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_FROM_CALLBACK
&redirect_uri=https://yoursite.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Response:

response.json
{
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer"
}

Auth codes expire after 60 seconds and are single-use.

4. Read the claims

Decode and verify the ID token (see Verify tokens below). The decoded payload:

id_token (decoded)
{
  "iss": "https://api.cageid.app",
  "sub": "anon_partner_scoped_hash",
  "aud": "YOUR_CLIENT_ID",
  "age_verified": true,
  "age_floor": 18,
  "iat": 1741910400,
  "exp": 1741996800
}
subAnonymous user ID — unique per partner. Cannot be correlated across partners.
age_verifiedAlways true if the token was issued.
age_floor18 or 21 — matches your registered age requirement.
issToken issuer — always https://api.cageid.app
audYour client_id
iat / expStandard issued-at and expiry timestamps.

SECURITY

Verify tokens

Always verify the JWT signature before trusting the claims. Use any standard OIDC/JWT library (jose, jsonwebtoken, etc.) with CAGE's public keys.

JWKS endpointhttps://api.cageid.app/oauth/.well-known/jwks.json
Discovery dochttps://api.cageid.app/oauth/.well-known/openid-configuration

Verify: signature, iss, aud (must equal your client_id), and exp.

verify.mjs (Node.js — jose)
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://api.cageid.app/oauth/.well-known/jwks.json')
);

const { payload } = await jwtVerify(idToken, JWKS, {
  issuer: 'https://api.cageid.app',
  audience: 'YOUR_CLIENT_ID',
});

console.log(payload.age_verified); // true
console.log(payload.age_floor);    // 18 or 21

OVERVIEW

User flow

1

User clicks ‘Verify Age’ on your site

2

Browser redirects to CAGE authorization endpoint

3

User logs in and consents — or the CAGE browser extension handles it silently

4

CAGE redirects back to your redirect_uri with an auth code

5

Your server exchanges the code for an ID token

6

Your server reads the age_verified and age_floor claims

REFERENCE

Configuration

Redirect URIs

Must be pre-registered with CAGE and match exactly — including trailing slashes and protocol. No wildcards.

http://localhost URIs are allowed for development and testing. Register them the same way as production URIs.

Age floor

Set during partner registration. Either 18 or 21. Contact us to change it.

Refresh tokens

Not supported at this time. Users re-authenticate when their session expires.

Auth code expiry

60 seconds. Single-use. Exchange immediately after receiving.

REFERENCE

Error handling

If the OAuth flow fails, CAGE redirects to your redirect_uri with error parameters per the OAuth 2.0 spec:

https://yoursite.com/callback
  ?error=access_denied
  &error_description=User+denied+consent
  &state=YOUR_ORIGINAL_STATE

Common error codes:

access_deniedUser clicked Deny on the consent screen
invalid_requestMissing or malformed OAuth parameters
invalid_clientUnrecognized client_id
invalid_redirect_uriredirect_uri doesn’t match registered URI
server_errorSomething went wrong on CAGE’s end

Your callback handler should check for the error query parameter before attempting to exchange the code.

Need help?

Email us at partners@cageid.app — we respond to integration questions within one business day.

The server source code is open source on GitHub if you want to inspect the exact endpoint behavior.