Skip to main content
This guide walks you through creating a complete FastAPI application with login and protected routes using AuthX.

Complete example

Here’s a minimal but fully functional authentication system:
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
from authx import AuthX, AuthXConfig

app = FastAPI()

# Configure AuthX
config = AuthXConfig(
    JWT_SECRET_KEY="your-secret-key",  # Change this!
    JWT_TOKEN_LOCATION=["headers"],
)

auth = AuthX(config=config)
auth.handle_errors(app)

# User model
class User(BaseModel):
    username: str
    password: str

# Sample user database
USERS = {
    "user1": {"password": "password1", "email": "user1@example.com"},
    "user2": {"password": "password2", "email": "user2@example.com"},
}

@app.post("/login")
def login(user: User):
    if user.username in USERS and USERS[user.username]["password"] == user.password:
        access_token = auth.create_access_token(user.username)
        return {"access_token": access_token, "token_type": "bearer"}
    raise HTTPException(status_code=401, detail="Invalid credentials")

@app.get("/protected", dependencies=[Depends(auth.access_token_required)])
def protected():
    return {"message": "You have access to this protected resource"}
Never hardcode secrets in production! Use environment variables to store your JWT_SECRET_KEY.

Step-by-step guide

Let’s build this authentication system step by step.
1

Create your FastAPI app

Start by creating a basic FastAPI application:
from fastapi import FastAPI

app = FastAPI(title="My Authenticated API")
2

Configure AuthX

Create an AuthXConfig instance with your settings:
from authx import AuthXConfig

config = AuthXConfig(
    JWT_SECRET_KEY="your-secret-key",  # Required for signing tokens
    JWT_ALGORITHM="HS256",              # Default algorithm
    JWT_TOKEN_LOCATION=["headers"],     # Accept tokens in headers
)
In production, load secrets from environment variables:
import os

config = AuthXConfig(
    JWT_SECRET_KEY=os.getenv("JWT_SECRET_KEY"),
)
3

Initialize AuthX

Create an AuthX instance and register error handlers:
from authx import AuthX

auth = AuthX(config=config)
auth.handle_errors(app)
The handle_errors() call ensures authentication errors return proper HTTP responses instead of 500 errors.
4

Create a login endpoint

Create an endpoint that validates credentials and returns a token:
from fastapi import HTTPException
from pydantic import BaseModel

class User(BaseModel):
    username: str
    password: str

USERS = {
    "user1": {"password": "password1", "email": "user1@example.com"},
}

@app.post("/login")
def login(user: User):
    if user.username in USERS and USERS[user.username]["password"] == user.password:
        access_token = auth.create_access_token(user.username)
        return {"access_token": access_token, "token_type": "bearer"}
    raise HTTPException(status_code=401, detail="Invalid credentials")
In a real application, use proper password hashing (bcrypt, argon2) and fetch users from a database.
5

Protect your routes

Use auth.access_token_required as a dependency to protect routes:
from fastapi import Depends

@app.get("/protected", dependencies=[Depends(auth.access_token_required)])
def protected():
    return {"message": "You have access to this protected resource"}
What happens:
  • No token401 Missing JWT in request
  • Invalid token401 Invalid Token
  • Valid token → Route executes normally

Run the application

Save your code to main.py and run it:
uvicorn main:app --reload
Your API is now running at http://localhost:8000.

Test the API

Let’s test the authentication flow:
1

Try accessing the protected route (should fail)

curl http://localhost:8000/protected
Response:
{
  "detail": "Missing JWT in request"
}
2

Login to get a token

curl -X POST "http://localhost:8000/login" \
  -H "Content-Type: application/json" \
  -d '{"username": "user1", "password": "password1"}'
Response:
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}
3

Access the protected route with the token

curl http://localhost:8000/protected \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
{
  "message": "You have access to this protected resource"
}

Get the current user

To access token information in your protected routes:
from fastapi import Depends
from authx import TokenPayload

@app.get("/me")
def get_current_user(payload: TokenPayload = Depends(auth.access_token_required)):
    return {
        "user_id": payload.sub,
        "email": USERS.get(payload.sub, {}).get("email")
    }
The TokenPayload contains:
  • sub - Subject (the user ID you passed to create_access_token)
  • exp - Expiration time
  • iat - Issued at time
  • type - Token type (“access” or “refresh”)
  • fresh - Whether the token is fresh
  • scopes - List of scopes (if provided)

Add refresh tokens

For longer sessions, use refresh tokens to issue new access tokens:
from fastapi import Request

@app.post("/login")
def login(user: User):
    if user.username in USERS and USERS[user.username]["password"] == user.password:
        access_token = auth.create_access_token(user.username)
        refresh_token = auth.create_refresh_token(user.username)
        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer"
        }
    raise HTTPException(status_code=401, detail="Invalid credentials")

@app.post("/refresh")
async def refresh(request: Request):
    refresh_token = await auth.get_refresh_token_from_request(request)
    payload = auth.verify_token(refresh_token, verify_type=True)
    
    # Create new access token
    access_token = auth.create_access_token(payload.sub)
    return {"access_token": access_token, "token_type": "bearer"}
Access tokens typically expire in 15 minutes, while refresh tokens can last for days or weeks.

Next steps

Now that you have basic authentication working, explore more features:

Token locations

Learn how to accept tokens from cookies, query parameters, or JSON body

Scopes

Implement fine-grained permissions with scope-based authorization

Token freshness

Require re-authentication for sensitive operations

Configuration

Explore all configuration options for customizing AuthX

Build docs developers (and LLMs) love