Integration
Python
AgeOnce integration with Python (Django, Flask, FastAPI)
Python Integration
Complete AgeOnce integration example with Python.
Install dependencies
pip install requests pyjwt cryptographyConfiguration
# 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.