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_APP_URL = os.environ.get('AGEONCE_APP_URL', 'https://app.ageonce.com')
AGEONCE_API_URL = os.environ.get('AGEONCE_API_URL', 'https://api.ageonce.com')Helper functions
# ageonce.py
import secrets
import base64
import requests
import jwt
from urllib.parse import urlencode, quote
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_APP_URL}/?{urlencode(params)}"
return url, state
def exchange_code_for_token(code, state=None):
"""Exchange authorization code for an access token."""
credentials = base64.b64encode(
f"{AGEONCE_CLIENT_ID}:{AGEONCE_CLIENT_SECRET}".encode()
).decode()
body = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': AGEONCE_REDIRECT_URI,
}
if state:
body['state'] = state
response = requests.post(
f"{AGEONCE_API_URL}/api/oauth/token",
headers={
'Content-Type': 'application/json',
'Authorization': f'Basic {credentials}',
},
json=body,
)
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.get(
f"{AGEONCE_API_URL}/api/verify/token/{quote(token, safe='')}"
)
data = response.json()
if response.status_code != 200 or not data.get('valid'):
return {'valid': False}
return {
'valid': True,
'age_verified': data.get('age_verified'),
'age_over': data.get('age_over'),
}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/.well-known/jwks.json")
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='https://ageonce.io',
)
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['access_token'])
if result['valid'] and result['age_verified']:
session['age_verified'] = True
session['age_over'] = result['age_over']
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['access_token'])
if result['valid'] and result['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['access_token'])
if result['valid'] and result['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.