Skip to main content

Use AI to integrate Auth0

If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:
npx skills add auth0/agent-skills
Then ask your AI assistant:
Add Auth0 authentication to my FastAPI app
Your AI assistant will automatically create your Auth0 application, fetch credentials, install auth0-fastapi, create authentication routes, and set up your configuration. Full agent skills documentation →
Prerequisites: Before you begin, ensure you have the following installed:
  • Python 3.9 or newer (3.11+ recommended)
  • pip 21+ or Poetry 1.2+
  • OpenSSL - Required for generating session secrets
FastAPI Version Compatibility: This quickstart requires FastAPI 0.115.11+ and Pydantic 2.12.5+.

Get Started

This quickstart demonstrates how to add Auth0 authentication to a Python FastAPI Web application. You’ll build a secure web app with login, logout, and user profile features using the Auth0 FastAPI SDK.
1

Create a new project

Create a new directory for your project and set up a virtual environment:
mkdir auth0-fastapi-app && cd auth0-fastapi-app
Create and activate a virtual environment:
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
2

Install the Auth0 FastAPI SDK

pip install auth0-fastapi "uvicorn[standard]" python-dotenv itsdangerous
3

Setup your Auth0 App

Next up, you need to create a new app on your Auth0 tenant and add the environment variables to your project.You can choose to do this automatically by running a CLI command or do it manually via the Dashboard:
Run the following shell command on your project’s root directory to create an Auth0 app and generate a .env file:
AUTH0_APP_NAME="My FastAPI App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && CLIENT_SECRET=$(jq -r '.client_secret' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && SESSION_SECRET=$(openssl rand -hex 64) && echo "AUTH0_DOMAIN=${DOMAIN}" > .env && echo "AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && echo "AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env && echo "SESSION_SECRET=${SESSION_SECRET}" >> .env && echo "APP_BASE_URL=http://localhost:3000" >> .env && rm auth0-app-details.json && echo ".env file created with your Auth0 details:" && cat .env
4

Configure the Auth0 FastAPI SDK

Create a main.py file in your project’s root directory and add the following code:
main.py
import os
from fastapi import FastAPI, Depends, Request, Response
from starlette.middleware.sessions import SessionMiddleware
from dotenv import load_dotenv

from auth0_fastapi.config import Auth0Config
from auth0_fastapi.auth.auth_client import AuthClient
from auth0_fastapi.server.routes import router, register_auth_routes

# Load environment variables
load_dotenv()

app = FastAPI(title="Auth0 FastAPI Example")

# Add Session Middleware - required for cookie handling
app.add_middleware(SessionMiddleware, secret_key=os.getenv("SESSION_SECRET"))

# Create Auth0Config with your Auth0 credentials
config = Auth0Config(
    domain=os.getenv("AUTH0_DOMAIN"),
    client_id=os.getenv("AUTH0_CLIENT_ID"),
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
    app_base_url=os.getenv("APP_BASE_URL", "http://localhost:3000"),
    secret=os.getenv("SESSION_SECRET"),
)

# Instantiate the AuthClient
auth_client = AuthClient(config)

# Attach to the FastAPI app state
app.state.config = config
app.state.auth_client = auth_client

# Register authentication routes
register_auth_routes(router, config)
app.include_router(router)
SESSION_SECRET is used to encrypt session cookies and must be cryptographically secure. Without a strong secret (minimum 32 bytes), your application’s sessions can be compromised. Generate a secure secret using openssl rand -hex 64 and never commit it to version control.SessionMiddleware must be added before using the SDK. Without it, FastAPI cannot read or set cookies, and all authentication attempts will fail silently.HTTPS in Production is required for secure cookies (secure=True). Without HTTPS, session cookies will not be sent by browsers, and users will be repeatedly logged out after each request.
5

Create Routes and Display User Profile

Add the following routes to your main.py file to create a home page and a protected profile page:
main.py
import os
from fastapi import FastAPI, Depends, Request, Response
from fastapi.responses import HTMLResponse
from starlette.middleware.sessions import SessionMiddleware
from dotenv import load_dotenv

from auth0_fastapi.config import Auth0Config
from auth0_fastapi.auth.auth_client import AuthClient
from auth0_fastapi.server.routes import router, register_auth_routes

# Load environment variables
load_dotenv()

app = FastAPI(title="Auth0 FastAPI Example")

# Add Session Middleware - required for cookie handling
app.add_middleware(SessionMiddleware, secret_key=os.getenv("SESSION_SECRET"))

# Create Auth0Config with your Auth0 credentials
config = Auth0Config(
    domain=os.getenv("AUTH0_DOMAIN"),
    client_id=os.getenv("AUTH0_CLIENT_ID"),
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
    app_base_url=os.getenv("APP_BASE_URL", "http://localhost:3000"),
    secret=os.getenv("SESSION_SECRET"),
    authorization_params={
      "scope": "openid profile email", # Required to get user profile information
    }
)

# Instantiate the AuthClient
auth_client = AuthClient(config)

# Attach to the FastAPI app state
app.state.config = config
app.state.auth_client = auth_client

# Register authentication routes
register_auth_routes(router, config)
app.include_router(router)


@app.get("/", response_class=HTMLResponse)
async def home(request: Request, response: Response):
    """Home page with login/logout buttons"""
    store_options = {"request": request, "response": response}
    session = await auth_client.client.get_session(store_options=store_options)

    if session:
        user = await auth_client.client.get_user(store_options=store_options)
        return f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>Auth0 FastAPI Example</title>
            <style>
                body {{
                    font-family: 'Inter', system-ui, -apple-system, sans-serif;
                    background-color: #1a1e27;
                    color: #e2e8f0;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    min-height: 100vh;
                    margin: 0;
                }}
                .container {{
                    background-color: #262a33;
                    border-radius: 20px;
                    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
                    padding: 3rem;
                    max-width: 500px;
                    width: 90%;
                    text-align: center;
                }}
                .logo {{
                    width: 160px;
                    margin-bottom: 1.5rem;
                }}
                h1 {{
                    font-size: 2.8rem;
                    font-weight: 700;
                    color: #f7fafc;
                    margin-bottom: 1rem;
                }}
                .success {{
                    font-size: 1.5rem;
                    color: #68d391;
                    font-weight: 600;
                    margin: 1.5rem 0;
                }}
                .profile {{
                    background-color: #2d313c;
                    border-radius: 15px;
                    padding: 2rem;
                    margin: 2rem 0;
                }}
                .profile-image {{
                    width: 110px;
                    height: 110px;
                    border-radius: 50%;
                    border: 3px solid #63b3ed;
                    margin-bottom: 1rem;
                }}
                .profile-name {{
                    font-size: 2rem;
                    font-weight: 600;
                    color: #f7fafc;
                    margin-bottom: 0.5rem;
                }}
                .profile-email {{
                    font-size: 1.15rem;
                    color: #a0aec0;
                }}
                .button {{
                    padding: 1.1rem 2.8rem;
                    font-size: 1.2rem;
                    font-weight: 600;
                    border-radius: 10px;
                    border: none;
                    cursor: pointer;
                    text-decoration: none;
                    display: inline-block;
                    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
                    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
                    text-transform: uppercase;
                    letter-spacing: 0.08em;
                }}
                .button.logout {{
                    background-color: #fc8181;
                    color: #1a1e27;
                }}
                .button.logout:hover {{
                    background-color: #e53e3e;
                    transform: translateY(-5px) scale(1.03);
                    box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
                }}
            </style>
        </head>
        <body>
            <div class="container">
                <img src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
                     alt="Auth0 Logo" class="logo">
                <h1>Welcome to Auth0 FastAPI</h1>
                <div class="success">✅ Successfully authenticated!</div>
                <h2>Your Profile</h2>
                <div class="profile">
                    <img src="{user.get('picture', '')}"
                         alt="{user.get('name', 'User')}" class="profile-image">
                    <div class="profile-name">{user.get('name', 'User')}</div>
                    <div class="profile-email">{user.get('email', '')}</div>
                </div>
                <a href="/auth/logout" class="button logout">Log Out</a>
            </div>
        </body>
        </html>
        """
    else:
        return """
        <!DOCTYPE html>
        <html>
        <head>
            <title>Auth0 FastAPI Example</title>
            <style>
                body {
                    font-family: 'Inter', system-ui, -apple-system, sans-serif;
                    background-color: #1a1e27;
                    color: #e2e8f0;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    min-height: 100vh;
                    margin: 0;
                }
                .container {
                    background-color: #262a33;
                    border-radius: 20px;
                    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
                    padding: 3rem;
                    max-width: 500px;
                    width: 90%;
                    text-align: center;
                }
                .logo {
                    width: 160px;
                    margin-bottom: 1.5rem;
                }
                h1 {
                    font-size: 2.8rem;
                    font-weight: 700;
                    color: #f7fafc;
                    margin-bottom: 1rem;
                }
                .action-card {
                    background-color: #2d313c;
                    border-radius: 15px;
                    padding: 2.5rem;
                    margin-top: 2rem;
                }
                .action-text {
                    font-size: 1.25rem;
                    color: #cbd5e0;
                    margin-bottom: 1.8rem;
                }
                .button {
                    padding: 1.1rem 2.8rem;
                    font-size: 1.2rem;
                    font-weight: 600;
                    border-radius: 10px;
                    border: none;
                    cursor: pointer;
                    text-decoration: none;
                    display: inline-block;
                    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
                    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
                    text-transform: uppercase;
                    letter-spacing: 0.08em;
                }
                .button.login {
                    background-color: #63b3ed;
                    color: #1a1e27;
                }
                .button.login:hover {
                    background-color: #4299e1;
                    transform: translateY(-5px) scale(1.03);
                    box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
                }
            </style>
        </head>
        <body>
            <div class="container">
                <img src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
                     alt="Auth0 Logo" class="logo">
                <h1>Welcome to Auth0 FastAPI</h1>
                <div class="action-card">
                    <p class="action-text">Get started by signing in to your account</p>
                    <a href="/auth/login" class="button login">Log In</a>
                </div>
            </div>
        </body>
        </html>
        """


@app.get("/profile")
async def profile(
    request: Request,
    response: Response,
    session=Depends(auth_client.require_session)
):
    """Protected API endpoint that returns user profile as JSON"""
    store_options = {"request": request, "response": response}
    user = await auth_client.client.get_user(store_options=store_options)

    return {
        "message": "Your Profile",
        "user": user,
        "session_details": session
    }


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=3000)
This creates:
  • A home page (/) that displays a login button when logged out, or the user’s profile when logged in
  • A protected API endpoint (/profile) that returns user data as JSON and requires authentication
  • Full styling for a polished user experience
6

Run your app

uvicorn main:app --reload --port 3000
Alternatively, if you added the if __name__ == "__main__" block to your main.py:
python main.py
CheckpointYou should now have a fully functional Auth0 login page running on your localhost.

Advanced Usage

Create custom FastAPI dependencies for role-based access control:
from fastapi import Depends, HTTPException, status
from typing import List

async def require_roles(required_roles: List[str]):
    """Dependency factory for role-based access control"""
    async def check_roles(
        request: Request,
        response: Response,
        session=Depends(auth_client.require_session)
    ):
        store_options = {"request": request, "response": response}
        user = await auth_client.client.get_user(store_options=store_options)

        user_roles = user.get("app_metadata", {}).get("roles", [])

        if not any(role in user_roles for role in required_roles):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Required roles: {', '.join(required_roles)}"
            )

        return session

    return check_roles

# Usage in routes
@app.get("/admin/dashboard")
async def admin_dashboard(
    session=Depends(require_roles(["admin", "superadmin"]))
):
    return {"message": "Admin dashboard access granted"}

@app.delete("/admin/users/{user_id}")
async def delete_user(
    user_id: str,
    session=Depends(require_roles(["admin"]))
):
    return {"message": f"User {user_id} deleted"}
Configure the SDK to request access tokens for your API and use them in downstream calls:
from auth0_fastapi.config import Auth0Config
import httpx

# Configure Auth0 with API audience
config = Auth0Config(
    domain=os.getenv("AUTH0_DOMAIN"),
    client_id=os.getenv("AUTH0_CLIENT_ID"),
    client_secret=os.getenv("AUTH0_CLIENT_SECRET"),
    app_base_url=os.getenv("APP_BASE_URL"),
    secret=os.getenv("SESSION_SECRET"),
    audience="https://api.example.com",  # Your API identifier
    authorization_params={
        "scope": "openid profile email read:data write:data",
    },
)

@app.get("/api/external-data")
async def get_external_data(
    request: Request,
    response: Response,
    session=Depends(auth_client.require_session)
):
    """Call external API with user's access token"""
    # Get access token from session
    access_token = session.get("access_token")

    if not access_token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="No access token available"
        )

    # Make authenticated API call
    async with httpx.AsyncClient() as client:
        api_response = await client.get(
            "https://api.example.com/data",
            headers={"Authorization": f"Bearer {access_token}"}
        )

        if api_response.status_code != 200:
            raise HTTPException(
                status_code=api_response.status_code,
                detail="External API call failed"
            )

    return {"data": api_response.json()}
Scale your application by storing sessions in Redis instead of encrypted cookies:
from auth0_fastapi.stores.stateful_state_store import StatefulStateStore
from redis import asyncio as aioredis

# Create Redis client
redis = await aioredis.from_url(
    "redis://localhost:6379",
    encoding="utf-8",
    decode_responses=True
)

# Create stateful store
state_store = StatefulStateStore(
    secret=os.getenv("SESSION_SECRET"),
    store=redis,
    cookie_name="_session_id",
    expiration=86400,  # 1 day in seconds
)

# Pass to AuthClient
auth_client = AuthClient(config, state_store=state_store)
Benefits of stateful sessions:
  • No cookie size limits - Store unlimited session data
  • Immediate invalidation - Delete sessions server-side
  • Backchannel logout support - Handle logout events from Auth0
  • Better for distributed systems - Share sessions across multiple servers

Troubleshooting

Problem: Users are logged in but sessions don’t persist across requests.Possible Causes & Solutions:
  1. Missing SessionMiddleware Ensure you’ve added SessionMiddleware to your app:
    from starlette.middleware.sessions import SessionMiddleware
    
    app.add_middleware(SessionMiddleware, secret_key=os.getenv("SESSION_SECRET"))
    
  2. HTTP in production with secure cookies Secure cookies require HTTPS. If testing locally over HTTP, you can temporarily disable secure cookies (not recommended for production):
    from auth0_fastapi.stores.stateless_state_store import StatelessStateStore
    
    state_store = StatelessStateStore(
        secret=config.secret,
        cookie_name="_a0_session",
        expiration=config.session_expiration
    )
    # Development only!
    state_store.cookie_options["secure"] = False
    
    auth_client = AuthClient(config, state_store=state_store)
    
  3. Weak or missing SESSION_SECRET Generate a strong secret:
    openssl rand -hex 64
    
Problem: After clicking “Log In”, Auth0 displays an error: “Callback URL mismatch”Cause: The callback URL is not registered in your Auth0 Application settings.Solution:
  1. Go to Auth0 Dashboard → Applications → Your App → Settings
  2. Add your callback URL to Allowed Callback URLs:
    http://localhost:3000/auth/callback
    
  3. For production, add your production URL:
    https://yourdomain.com/auth/callback
    
  4. Click Save Changes
Note: The URL must match exactly, including protocol (http/https) and port number.
Problem: Errors related to async functions or event loops.Cause: FastAPI is an async framework and all SDK methods must be awaited.Solution: Ensure all route functions are async and SDK methods are properly awaited:
# ✅ Correct
@app.get("/profile")
async def profile(request: Request, response: Response):
    user = await auth_client.client.get_user(
        store_options={"request": request, "response": response}
    )
    return {"user": user}

# ❌ Incorrect - missing async
@app.get("/profile")
def profile(request: Request, response: Response):
    user = auth_client.client.get_user(...)  # This will fail
    return {"user": user}

# ❌ Incorrect - missing await
@app.get("/profile")
async def profile(request: Request, response: Response):
    user = auth_client.client.get_user(...)  # Returns coroutine, not data
    return {"user": user}
Problem: Sessions work locally but not in production.Cause: Secure cookies require HTTPS in production. The secure=True flag prevents cookies from being sent over unencrypted HTTP connections.Solution:
  1. Configure HTTPS on your production server using:
    • Let’s Encrypt certificates
    • Cloud provider SSL/TLS (AWS ALB, Cloudflare, etc.)
    • Reverse proxy (Nginx, Caddy, Traefik)
  2. Update your Auth0 Application URLs to use HTTPS:
    https://yourdomain.com/auth/callback
    https://yourdomain.com
    
  3. Ensure APP_BASE_URL uses HTTPS:
    APP_BASE_URL=https://yourdomain.com
    
For more advanced features and configuration options, check out the Auth0 FastAPI SDK documentation.