This example demonstrates how to integrate JWT middleware with your custom FastAPI application and then add AgentOS functionality on top.

Code

custom_fastapi_jwt.py
from datetime import datetime, timedelta, UTC

import jwt
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.models.openai import OpenAIChat
from agno.os import AgentOS
from agno.os.middleware import JWTMiddleware
from agno.tools.duckduckgo import DuckDuckGoTools
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=OpenAIChat(id="gpt-4o"),
    db=db,
    tools=[DuckDuckGoTools()],
    add_history_to_context=True,
    markdown=True,
)

# Create custom FastAPI app
app = FastAPI(
    title="Example Custom App",
    version="1.0.0",
)

# Add Agno JWT middleware to your custom FastAPI app
app.add_middleware(
    JWTMiddleware,
    secret_key=JWT_SECRET,
    excluded_route_paths=[
        "/auth/login"
    ],  # We don't want to validate the token for the login endpoint
    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")


# Clean AgentOS setup with tuple middleware pattern! ✨
agent_os = AgentOS(
    description="JWT Protected AgentOS",
    agents=[research_agent],
    fastapi_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

1

Create a virtual environment

Open the Terminal and create a python virtual environment.
python3 -m venv .venv
source .venv/bin/activate
2

Set Environment Variables

export OPENAI_API_KEY=your_openai_api_key
3

Install libraries

pip install -U agno openai pyjwt ddgs fastapi["standard"] uvicorn sqlalchemy pgvector psycopg python-multipart
4

Setup 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:pg17
5

Run Example

python custom_fastapi_jwt.py
6

Test 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/json" \
  -d '{"message": "Search for information about FastAPI middleware"}'
Step 3: Test without token (should get 401)
curl "http://localhost:7777/config"
# Should return: {"detail": "Not authenticated"}
7

Test on a browser

  1. Visit the API docs: http://localhost:7777/docs
  2. Login via form: Try the /auth/login endpoint with username=demo and password=password
  3. Copy the token: From the response, copy the access_token value
  4. Authorize in docs: Click the “Authorize” button and paste Bearer <your-token>
  5. Test protected endpoints: Try any AgentOS endpoint - they should now work

Authentication Flow

1

User Login

Client sends credentials to /auth/login:
POST /auth/login
Content-Type: application/x-www-form-urlencoded

username=demo&password=password
2

Token Generation

Server validates credentials and returns JWT:
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
  "token_type": "bearer"
}
3

Authenticated Requests

Client includes token in Authorization header:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
4

Middleware Validation

JWT middleware validates token and allows/denies access

Developer Resources