How to Add JWT Authentication to a FastAPI API

JWT authentication on a FastAPI API means two things: a login endpoint that signs and returns a token, and a dependency that verifies the bearer token on every protected route. Here's how to wire both using PyJWT.

Backend Engineerpythonfastapijwt

How JWT auth fits into FastAPI

The pattern has two parts. First, POST /auth/login validates credentials and returns a signed JWT. Second, a require_auth dependency reads the Authorization: Bearer <token> header on protected routes, decodes and verifies the token with PyJWT, and raises HTTPException(401) if anything is wrong. Routes that don't need auth - /health, /auth/login - simply don't declare the dependency.

Issue a signed token on login

import os
from datetime import datetime, timedelta, timezone
import jwt
from fastapi import FastAPI, HTTPException

app = FastAPI()
JWT_SECRET = os.environ["JWT_SECRET"]
JWT_ALGORITHM = "HS256"

@app.post("/auth/login")
def login(credentials: dict):
    if credentials.get("username") != "alice" or credentials.get("password") != "s3cret":
        raise HTTPException(status_code=401, detail="Invalid credentials")
    payload = {
        "sub": credentials["username"],
        "iat": datetime.now(timezone.utc),
        "exp": datetime.now(timezone.utc) + timedelta(hours=1),
    }
    token = jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
    return {"token": token}

sub (subject) stores the username in the token claims. exp sets the expiry - PyJWT enforces it automatically on decode.

Verify the token in a dependency

from fastapi import Depends, Header

def require_auth(authorization: str = Header(None)) -> dict:
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Missing bearer token")
    token = authorization.removeprefix("Bearer ").strip()
    try:
        return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")

@app.get("/api/users")
def list_users(claims: dict = Depends(require_auth)):
    return {"users": [...]}

@app.get("/api/users/me")
def get_me(claims: dict = Depends(require_auth)):
    return {"username": claims["sub"]}

Depends(require_auth) injects the decoded claims into the route handler. You get the username from claims["sub"] without another DB lookup.

Test the full flow

# Start the server
uvicorn main:app --host 0.0.0.0 --port 8000

# Login and capture the token
TOKEN=$(curl -sf -X POST http://localhost:8000/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"s3cret"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')

# Authenticated call
curl http://localhost:8000/api/users -H "Authorization: Bearer $TOKEN"

# No token - should return 401
curl -i http://localhost:8000/api/users

What to know before production

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

How do I protect FastAPI routes with JWT?

Write a dependency function that reads the Authorization header, strips 'Bearer ', and calls jwt.decode. Wire it to protected routes with Depends(require_auth). Public routes like /health simply don't declare the dependency.

How do I sign a JWT with PyJWT?

Call jwt.encode({'sub': username, 'exp': datetime.now(timezone.utc) + timedelta(hours=1)}, secret, algorithm='HS256'). PyJWT returns the token string you can return in the response body.

What errors does jwt.decode raise for bad tokens?

PyJWT raises ExpiredSignatureError for tokens past their exp claim, and InvalidTokenError (the base class) for wrong signatures, missing fields, or malformed tokens. Catch both and return 401.

Which is better, OAuth or JWT?

They solve different problems. OAuth is an authorization framework (how a user grants an app access); JWT is a token format often used to carry that authorization. For a simple API login, JWT alone is enough; OAuth often issues a JWT.

Keep learning

Return the Correct HTTP Status CodeBackend projectRepair Broken API PaginationBackend projectAdd CORS Middleware to an Express APIBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →