Unlock Your Python Backend Career: Build 30 Projects in 30 Days. Join now for just $54

Securing Your FastAPI APIs with JWT

by Jane Nkwor

.

Updated Tue Oct 14 2025

.
Securing Your FastAPI APIs with JWT

In the previous article, we covered the basics of authentication in FastAPI, including the use of sessions, API keys, and basic HTTP authentication. Now, let’s take it a step further and talk about JWT (JSON Web Token) — one of the most popular ways to secure modern APIs.

What is JWT?

JSON Web Tokens (JWTs) are like digital ID cards. They are an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWTs are often used for authentication and information exchange in web development.

When you log in, the server assigns you an ID (the token), which you must display on every request to prove your identity.

Unlike traditional sessions, JWTs don’t need the server to “remember” you — everything it needs is inside the token itself.

How JWT Works

JWT authentication follows these steps:

  1. User logs in with credentials (username/password)

  2. Server validates credentials and generates a JWT token

  3. The token is returned to the client

  4. Client stores the token (usually in local storage or cookies)

  5. For subsequent requests, the client sends the token in the Authorisation header

  6. The server validates the token and processes the request if valid

JWT Structure

A JSON web token has three parts, separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqYW5lZG9lIiwiZXhwIjoxNzYwMDQ2MTgwfQ.xWdrV_-y3Fnsii_IdIzDXaUjb05FL2-CPal5iwJ_UlU
  1. Header(green) – tells which algorithm was used to sign the token.

  2. Payload(red) – contains your data (like username or role).

  3. Signature(blue) – ensures the token hasn’t been changed.

Example:

{
  "sub": "janedoe",
  "role": "user",
  "exp": 1716242622
}

Why JWTs are so popular

Stateless

Unlike traditional session-based authentication, JWTs are completely stateless.

There’s no need to store session information in memory or a database — the token itself contains everything the server needs to know about the user.

This means:

  • No need to “remember” users on the backend

  • Easier horizontal scaling (add more servers without worrying about shared sessions)

  • Reduced load on your database or cache

Scalable

Because they don’t rely on centralised session storage, JWTs are ideal for distributed systems or microservice architectures. Each service can independently verify the same token using a shared secret or public key — no coordination required.

That’s why large-scale platforms like Google, GitHub, and Auth0 rely on JWT-based authentication for millions of users daily.

Fast

Authenticating with JWTs is blazing fast.

The server only needs to verify the token’s signature ****and ****decode its payload ****— no database lookup or session validation required.

This stateless design significantly improves performance, especially under high traffic.

Cross-platform

JWTs are based on open standards (RFC 7519), making them universally compatible across languages, frameworks, and platforms.

Whether your frontend is built in React, Flutter, or Vue, and your backend runs on FastAPI, Node.js, or Go — JWTs work seamlessly for all.

But….

While JWTs offer major advantages, they come with trade-offs you must understand to use them securely and efficiently:

Hard to Invalidate Immediately

Once issued, JWTs remain valid until they expire — unless you implement a token blacklist or revocation mechanism.

If a user logs out or a token is compromised, there’s no easy way to instantly revoke it without additional logic (such as maintaining a deny list or rotating signing keys).

Can Get Large

JWTs contain encoded data (header, payload, signature) in Base64 format.

The more claims you add — user info, roles, permissions — the larger the token becomes.

This can slightly increase request size, especially for mobile or bandwidth-limited clients.

Must Be Stored Securely

JWTs often contain sensitive information and access rights.

If stored improperly (like in localStorage), they can be stolen via XSS attacks.

Safer alternatives include:

  • HTTP-only cookies (cannot be accessed by JavaScript)

  • Secure client storage mechanisms

    Always combine this with HTTPS and proper token expiration.

Implementing JWT Authentication in FastAPI

Setting up JWT in FastAPI

pip install fastapi uvicorn[standard] python-jose[cryptography] passlib[bcrypt] python-multipart

Basic Project Structure

app/
├── main.py
├── auth/
│   ├── jwt_handler.py
│   └── routes.py
└── models/
    └── user.py

Step 1: Create and Verify Tokens

# app/auth/jwt_handler.py
from datetime import datetime, timedelta
from jose import jwt, JWTError

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"

def create_access_token(data: dict, expires_minutes: int = 30):
    data_copy = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=expires_minutes)
    data_copy.update({"exp": expire})
    return jwt.encode(data_copy, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(token: str):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        return None

Step 2: Create Auth Routes

# app/auth/routes.py
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from passlib.context import CryptContext
from .jwt_handler import create_access_token, verify_token

router = APIRouter(prefix="/auth", tags=["Auth"])
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# Mock database
fake_users = {
    "janedoe": {
        "username": "janedoe",
        "hashed_password": pwd_context.hash("password123")
    }
}

@router.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = fake_users.get(form_data.username)
    if not user or not pwd_context.verify(form_data.password, user["hashed_password"]):
        raise HTTPException(status_code=401, detail="Invalid username or password")

    token = create_access_token({"sub": user["username"]})
    return {"access_token": token, "token_type": "bearer"}

@router.get("/verify")
def verify(token: str):
    data = verify_token(token)
    if not data:
        raise HTTPException(status_code=401, detail="Invalid or expired token")
    return {"user": data["sub"]}

Step 3: Protect Your Routes

# app/main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from app.auth.jwt_handler import verify_token
from app.auth.routes import router as auth_router

app = FastAPI(title="JWT Auth Demo")
app.include_router(auth_router)

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")

def get_current_user(token: str = Depends(oauth2_scheme)):
    data = verify_token(token)
    if not data:
        raise HTTPException(status_code=401, detail="Invalid or expired token")
    return data

@app.get("/")
def home():
    return {"message": "Welcome to the public route"}

@app.get("/dashboard")
def dashboard(current_user: dict = Depends(get_current_user)):
    return {"message": f"Welcome {current_user['sub']}! This is a protected route."}

Step 4: Test your JWT System

uvicorn app.main:app --reload

Then test your API with Swagger:

  1. Go to localhost:8000

  2. Post auth/login → get the token

  3. Copy the token and send it in the headers:

Authorization: Bearer <your_token_here>

4. Go to /dashboard → should return your username.

Best Practices for API Security

  • Use HTTPS: Always use HTTPS to encrypt data in transit

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board

Backend Tips, Every week

Backend Tips, Every week