How it works
Detailed overview of AgeOnce age verification flow
How AgeOnce Works
AgeOnce uses the standard OAuth 2.0 Authorization Code Flow for age verification. This ensures security and compatibility with most systems.
Flow Overview
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Your site │ │ AgeOnce │ │ User │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ 1. Redirect │ │
│──────────────────►│ │
│ │ 2. Verification │
│ │◄─────────────────►│
│ │ │
│ 3. Callback+code │ │
│◄──────────────────│ │
│ │ │
│ 4. Token request │ │
│──────────────────►│ │
│ │ │
│ 5. JWT token │ │
│◄──────────────────│ │
│ │ │
│ 6. Access │ │
│───────────────────────────────────────►Detailed Steps
1. Initiate verification
When a user attempts to access age-restricted content, your site redirects them to AgeOnce:
https://app.ageonce.com/verify?client_id=...&redirect_uri=...&state=...Parameters:
client_id— unique identifier of your clientredirect_uri— where to return the user after verificationstate— random string for CSRF attack protection
2. Biometric verification
On the AgeOnce page, the user undergoes biometric age verification:
- User grants camera permission
- System analyzes biometric data
- Age compliance is determined
Privacy: Biometric data is processed in real-time and not stored. We only store the fact of successful verification.
3. Callback with authorization code
After successful verification, the user is redirected back to your site:
https://yoursite.com/callback?code=abc123&state=xyz789Important: Always verify that state matches what you sent!
4. Exchange code for token
Your backend exchanges the authorization code for an age token:
POST /api/oauth/token
Content-Type: application/json
{
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"code": "authorization_code",
"redirect_uri": "https://yoursite.com/callback"
}The authorization code can only be used once and is valid for 1 minute.
5. Receive JWT token
The response contains a JWT token with age information:
{
"age_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 600,
"transaction_id": "550e8400-e29b-41d4-a716-446655440000"
}Response fields:
transaction_id— Audit ID for compliance; searchable in Dashboard Audit Logs
Token structure (payload):
{
"sub": "anonymous",
"age_verified": true,
"min_age": 18,
"age_over": 18,
"verification_id": "550e8400-e29b-41d4-a716-446655440000",
"verified_at": "2026-02-11T12:00:00Z",
"client_id": "your_client_id",
"iat": 1739275200,
"exp": 1739361600,
"iss": "ageonce"
}Payload fields:
verification_id— same astransaction_id; use for audit trail
6. Grant access
Based on age_verified, you decide whether to grant access to content:
if (payload.age_verified && payload.min_age >= 18) {
// Grant access to 18+ content
}Token Validation
Option A: Via API
POST /api/oauth/validate
Content-Type: application/json
{
"token": "eyJhbGciOiJSUzI1NiIs..."
}Advantages: Simpler, no need to store keys
Disadvantages: Additional HTTP request
Option B: Local validation
- Get the public key from
/api/oauth/jwks - Validate JWT signature locally
Advantages: Faster, less API load
Disadvantages: Need to cache and update keys
Security
JWT signature
Tokens are signed with RS256 algorithm (RSA + SHA-256). This ensures:
- Token cannot be forged
- Token cannot be modified
Lifetime
- Authorization code: 1 minute
- Age token: 10 minutes
Recommendations
- Always verify the
stateparameter - Store
client_secretsecurely (server-side only) - Validate tokens before use
- Use HTTPS for all requests