Custom FastAPI App with JWT Middleware
Custom FastAPI application with JWT middleware for authentication and AgentOS integration
Add AuthMiddleware to your own FastAPI app, then pass the app to AgentOS as base_app. The middleware covers your routes and the AgentOS routes.
AuthMiddleware was named JWTMiddleware before v2.7. JWTMiddleware still works as an alias.
Code
from datetime import datetime, timedelta, UTC
import jwt
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS
from agno.os.middleware.jwt import AuthMiddleware
from agno.tools.hackernews import HackerNewsTools
from fastapi import FastAPI, Form, HTTPException
# JWT Secret (use environment variable in production)
JWT_SECRET = "a-string-secret-at-least-256-bits-long"
# Setup database
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
# Create agent
research_agent = Agent(
id="research-agent",
name="Research Agent",
model=OpenAIResponses(id="gpt-5.2"),
db=db,
tools=[HackerNewsTools()],
add_history_to_context=True,
markdown=True,
)
# Create custom FastAPI app
app = FastAPI(
title="Example Custom App",
version="1.0.0",
)
# Add the Agno auth middleware to your custom FastAPI app
app.add_middleware(
AuthMiddleware,
verification_keys=[JWT_SECRET],
algorithm="HS256", # The default is RS256. Match the algorithm used to sign the token.
excluded_route_paths=[
"/auth/login",
"/docs",
"/openapi.json",
], # Skip token validation for the login endpoint and the API docs
validate=True, # Set validate to False to skip token validation
)
# Custom routes that use JWT
@app.post("/auth/login")
async def login(username: str = Form(...), password: str = Form(...)):
"""Login endpoint that returns JWT token"""
if username == "demo" and password == "password":
payload = {
"sub": "user_123",
"username": username,
"exp": datetime.now(UTC) + timedelta(hours=24),
"iat": datetime.now(UTC),
}
token = jwt.encode(payload, JWT_SECRET, algorithm="HS256")
return {"access_token": token, "token_type": "bearer"}
raise HTTPException(status_code=401, detail="Invalid credentials")
agent_os = AgentOS(
description="JWT Protected AgentOS",
agents=[research_agent],
base_app=app,
)
# Get the final app
app = agent_os.get_app()
if __name__ == "__main__":
"""
Run your AgentOS with JWT middleware applied to the entire app.
Test endpoints:
1. POST /auth/login - Login to get JWT token
2. GET /config - Protected route (requires JWT)
"""
agent_os.serve(
app="custom_fastapi_jwt:app", port=7777, reload=True
)Usage
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activateSet Environment Variables
export OPENAI_API_KEY=your_openai_api_keyInstall dependencies
uv pip install -U agno openai pyjwt "fastapi[standard]" uvicorn sqlalchemy pgvector "psycopg[binary]" python-multipartSetup PostgreSQL Database
# Using Docker
docker run -d \
--name agno-postgres \
-e POSTGRES_DB=ai \
-e POSTGRES_USER=ai \
-e POSTGRES_PASSWORD=ai \
-p 5532:5432 \
pgvector/pgvector:pg17Run Example
python custom_fastapi_jwt.pyTest Authentication Flow
Step 1: Login to get JWT token
TOKEN=$(curl -X POST "http://localhost:7777/auth/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=demo&password=password" \
| jq -r '.access_token')
echo "Token: $TOKEN"Step 2: Test protected endpoints with token
# Test AgentOS config endpoint
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:7777/config"
# Test agent interaction
curl -X POST "http://localhost:7777/agents/research-agent/runs" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "message=Search for information about FastAPI middleware"Step 3: Test without token (should get 401)
curl "http://localhost:7777/config"
# Should return: {"detail": "Authorization header missing"}Test on a browser
- Visit the API docs: http://localhost:7777/docs
- Login via form: Try the
/auth/loginendpoint withusername=demoandpassword=password - Copy the token: From the response, copy the
access_tokenvalue - Authorize in docs: Click the "Authorize" button and paste the token. Swagger adds the
Bearerprefix for you. - Test protected endpoints: Try any AgentOS endpoint. They should now work.
Authentication Flow
User Login
Client sends credentials to /auth/login:
POST /auth/login
Content-Type: application/x-www-form-urlencoded
username=demo&password=passwordToken Generation
Server validates credentials and returns JWT:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer"
}Authenticated Requests
Client includes token in Authorization header:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...Middleware Validation
The auth middleware validates the token and allows or denies access.