AgeOnce Docs
Integration

Python

AgeOnce integration with Python (Django, Flask, FastAPI)

Python Integration

Complete AgeOnce integration example with Python.

Install dependencies

pip install requests pyjwt cryptography

Configuration

# config.py
import os

AGEONCE_CLIENT_ID = os.environ.get('AGEONCE_CLIENT_ID')
AGEONCE_CLIENT_SECRET = os.environ.get('AGEONCE_CLIENT_SECRET')
AGEONCE_REDIRECT_URI = os.environ.get('AGEONCE_REDIRECT_URI')
AGEONCE_API_URL = os.environ.get('AGEONCE_API_URL', 'https://app.ageonce.com')

Helper functions

# ageonce.py
import secrets
import requests
import jwt
from config import *

def generate_verify_url():
    """Generate verification URL and state token."""
    state = secrets.token_hex(16)
    
    params = {
        'client_id': AGEONCE_CLIENT_ID,
        'redirect_uri': AGEONCE_REDIRECT_URI,
        'state': state,
    }
    
    url = f"{AGEONCE_API_URL}/verify?{'&'.join(f'{k}={v}' for k, v in params.items())}"
    
    return url, state

def exchange_code_for_token(code):
    """Exchange authorization code for age token."""
    response = requests.post(
        f"{AGEONCE_API_URL}/api/oauth/token",
        json={
            'client_id': AGEONCE_CLIENT_ID,
            'client_secret': AGEONCE_CLIENT_SECRET,
            'code': code,
            'redirect_uri': AGEONCE_REDIRECT_URI,
        }
    )
    
    if response.status_code != 200:
        raise Exception(response.json().get('error_description', 'Token exchange failed'))
    
    return response.json()

def validate_token_via_api(token):
    """Validate token via API."""
    response = requests.post(
        f"{AGEONCE_API_URL}/api/oauth/validate",
        json={'token': token}
    )
    
    if response.status_code != 200:
        return {'valid': False}
    
    data = response.json()
    return {
        'valid': data.get('valid', False),
        'payload': data.get('payload'),
    }

Local JWT validation

import jwt
import requests
from functools import lru_cache

@lru_cache(maxsize=1)
def get_public_key():
    """Fetch and cache public key."""
    response = requests.get(f"{AGEONCE_API_URL}/api/oauth/jwks")
    jwks = response.json()
    
    # Get first key
    key_data = jwks['keys'][0]
    
    # Convert JWK to PEM
    from jwt.algorithms import RSAAlgorithm
    public_key = RSAAlgorithm.from_jwk(key_data)
    
    return public_key

def validate_token_locally(token):
    """Validate JWT token locally."""
    try:
        public_key = get_public_key()
        
        payload = jwt.decode(
            token,
            public_key,
            algorithms=['RS256'],
            issuer='ageonce',
        )
        
        return {'valid': True, 'payload': payload}
    except jwt.exceptions.InvalidTokenError as e:
        return {'valid': False, 'error': str(e)}

Flask example

from flask import Flask, redirect, request, session, url_for
from ageonce import generate_verify_url, exchange_code_for_token, validate_token_via_api

app = Flask(__name__)
app.secret_key = 'your-secret-key'

@app.route('/verify')
def verify():
    url, state = generate_verify_url()
    session['oauth_state'] = state
    return redirect(url)

@app.route('/callback')
def callback():
    code = request.args.get('code')
    state = request.args.get('state')
    
    # Verify state
    if state != session.get('oauth_state'):
        return 'Invalid state', 400
    
    try:
        data = exchange_code_for_token(code)
        result = validate_token_via_api(data['age_token'])
        
        if result['valid'] and result['payload']['age_verified']:
            session['age_verified'] = True
            session['min_age'] = result['payload']['min_age']
            return redirect('/protected')
        else:
            return 'Age verification failed', 403
    except Exception as e:
        return f'Error: {e}', 500

def require_age_verification(f):
    from functools import wraps
    @wraps(f)
    def decorated(*args, **kwargs):
        if not session.get('age_verified'):
            return redirect(url_for('verify'))
        return f(*args, **kwargs)
    return decorated

@app.route('/protected')
@require_age_verification
def protected():
    return 'Welcome to age-restricted content!'

if __name__ == '__main__':
    app.run(port=3000)
# views.py
from django.shortcuts import redirect
from django.http import HttpResponse
from ageonce import generate_verify_url, exchange_code_for_token, validate_token_via_api

def verify(request):
    url, state = generate_verify_url()
    request.session['oauth_state'] = state
    return redirect(url)

def callback(request):
    code = request.GET.get('code')
    state = request.GET.get('state')
    
    if state != request.session.get('oauth_state'):
        return HttpResponse('Invalid state', status=400)
    
    try:
        data = exchange_code_for_token(code)
        result = validate_token_via_api(data['age_token'])
        
        if result['valid'] and result['payload']['age_verified']:
            request.session['age_verified'] = True
            return redirect('/protected/')
        else:
            return HttpResponse('Age verification failed', status=403)
    except Exception as e:
        return HttpResponse(f'Error: {e}', status=500)

# Decorator for views
from functools import wraps

def age_verified_required(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if not request.session.get('age_verified'):
            return redirect('/verify/')
        return view_func(request, *args, **kwargs)
    return wrapper

@age_verified_required
def protected(request):
    return HttpResponse('Welcome to age-restricted content!')
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import RedirectResponse
from starlette.middleware.sessions import SessionMiddleware
from ageonce import generate_verify_url, exchange_code_for_token, validate_token_via_api

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key='your-secret-key')

@app.get('/verify')
async def verify(request: Request):
    url, state = generate_verify_url()
    request.session['oauth_state'] = state
    return RedirectResponse(url)

@app.get('/callback')
async def callback(request: Request, code: str, state: str):
    if state != request.session.get('oauth_state'):
        raise HTTPException(status_code=400, detail='Invalid state')
    
    try:
        data = exchange_code_for_token(code)
        result = validate_token_via_api(data['age_token'])
        
        if result['valid'] and result['payload']['age_verified']:
            request.session['age_verified'] = True
            return RedirectResponse('/protected')
        else:
            raise HTTPException(status_code=403, detail='Age verification failed')
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get('/protected')
async def protected(request: Request):
    if not request.session.get('age_verified'):
        return RedirectResponse('/verify')
    return {'message': 'Welcome to age-restricted content!'}

These examples can be adapted for any Python framework.

On this page