Python (FastAPI)
How to authenticate AI agents in Python and FastAPI using agentonboard-sdk and public RS256 JWKS keys.
AgentOnboard identity tokens are standard RS256 JWTs signed by our identity engine.
You can verify them using the official agentonboard-sdk Python library, or directly with native pyjwt.
1. Install the Python SDK
pip install "agentonboard-sdk[fastapi]"2. Guard your FastAPI routes
The SDK provides AgentBearerAuth, a FastAPI security dependency that handles token extraction, RS256 JWKS key caching, and 401/403 status mappings automatically.
Basic Token Authentication
from fastapi import Depends, FastAPI
from agentonboard import AgentIdentity
from agentonboard.frameworks.fastapi import AgentBearerAuth
app = FastAPI(title="Notes API")
# Configure verifier with your service domain
agent_auth = AgentBearerAuth(audience="notes.com")
@app.get("/api/notes")
async def list_notes(identity: AgentIdentity = Depends(agent_auth)):
# identity.email contains the verified human's email address
return {"message": f"Hello, {identity.email}"}With Database Account Resolution & Auto-Provisioning
Use resolver to look up your existing database user, and optional creator to automatically provision accounts for new agents:
from fastapi import Depends, FastAPI
from agentonboard import AgentIdentity
from agentonboard.frameworks.fastapi import AgentBearerAuth
app = FastAPI(title="Notes API")
# Database lookups
async def get_user_by_email(identity: AgentIdentity):
return await db.users.find_one({"email": identity.email.lower()})
async def create_user_for_agent(identity: AgentIdentity):
return await db.users.insert_one({"email": identity.email.lower(), "role": "agent_user"})
agent_auth = AgentBearerAuth(
audience="notes.com",
resolver=get_user_by_email,
creator=create_user_for_agent,
auto_create=True, # Set False to require existing account (returns 403 ACCOUNT_REQUIRED)
)
@app.post("/api/notes")
async def create_note(user = Depends(agent_auth)):
return {"success": True, "user_id": str(user["_id"])}3. Standalone Verification (Flask / Django / Scripts)
You can also call the verifier directly in synchronous or asynchronous code:
from agentonboard import verify_agent_token, verify_agent_account
# Sync verification
result = verify_agent_token(
token=bearer_token,
audience="notes.com",
)
if result.ok:
print(f"Verified human: {result.email}")
else:
print(f"Auth failed [{result.code}]: {result.error}")For Flask apps:
from flask import Flask, jsonify, g
from agentonboard.frameworks.flask import require_agent_token
app = Flask(__name__)
@app.route("/api/notes")
@require_agent_token(audience="notes.com")
def get_notes():
return jsonify({"email": g.agent_identity.email})4. Alternative: Native PyJWT (Without SDK)
If you prefer not to add a third-party SDK dependency, you can verify tokens using standard pyjwt:
import jwt
from jwt import PyJWKClient
from fastapi import FastAPI, Header, HTTPException, Depends, status
app = FastAPI(title="Notes API")
JWKS_URL = "https://agentonboard.xyz/.well-known/jwks.json"
AUDIENCE = "notes.com"
jwks_client = PyJWKClient(JWKS_URL, cache_keys=True, max_cached_keys=16)
async def require_agent_user(authorization: str = Header(...)):
if not authorization.startswith("Bearer "):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail={"code": "MALFORMED_TOKEN", "error": "Bearer token required"}
)
token = authorization.removeprefix("Bearer ").strip()
try:
signing_key = jwks_client.get_signing_key_from_jwt(token)
payload = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience=AUDIENCE,
leeway=5
)
email = payload.get("email")
if not email:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail={"code": "MISSING_EMAIL", "error": "Token missing email claim"}
)
return email
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail={"code": "EXPIRED", "error": "Token expired"})
except Exception as err:
raise HTTPException(status_code=401, detail={"code": "INVALID_SIGNATURE", "error": str(err)})5. The 401 vs 403 contract
Follow the AgentOnboard error specification:
- Return 401 Unauthorized for invalid, expired, or malformed tokens. The agent will re-mint a fresh token once via
get_tokenand retry. - Return 403 Forbidden with
{"code": "ACCOUNT_REQUIRED"}when the token is cryptographically valid but the email has no account. The agent stops and directs the human user to connect their account.
6. Test with curl
# Mint a 5-minute test token
TOKEN=$(curl -s -X POST https://agentonboard.xyz/api/v1/token/exchange \
-H "Authorization: Bearer aon_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"audience": "notes.com"}' | jq -r .token)
# Call your FastAPI endpoint
curl -i -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/notes