Implementing OAuth2 and JWT in FastAPI

Implementing OAuth2 and JWT in FastAPI

Build a secure login system. Learn how to use FastAPI's built-in OAuth2 support to issue and verify JWT tokens.

Implementing OAuth2 and JWT in FastAPI

Theory is great, but now it's time to build. FastAPI provides first-class support for OAuth2 (the industry-standard protocol for authorization). It even provides the logic to extract tokens from headers automatically.

In this lesson, we build a production-ready authentication flow.


1. The OAuth2 Password Flow

This is the standard flow for a web or mobile application where the user provides a username and password directly to your API.

The Steps:

  1. User sends credentials to /token.
  2. Server verifies credentials.
  3. Server issues a JWT.
  4. User sends the JWT in the Authorization: Bearer <TOKEN> header for all future requests.

2. Setting up the OAuth2 Scheme

FastAPI gives us OAuth2PasswordBearer. This is a dependency that:

  • Looks for the Authorization header.
  • Checks if it starts with Bearer.
  • Extracts the token.
  • If it's missing, returns a 401 Unauthorized automatically.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

3. Creating and Verifying JWTs

You will typically use a library like python-jose to handle the cryptographic heavy lifting.

Creating a Token:

from jose import jwt
from datetime import datetime, timedelta

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

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

4. The get_current_user Dependency

The most elegant way to handle auth in FastAPI is to create a dependency that decodes the token and returns a User object.

async def get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        user = db.get_user(username)
        return user
    except JWTError:
        raise credentials_exception

@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

Swagger Integration (The "Magic")

Because you are using OAuth2PasswordBearer, your Swagger UI (/docs) will automatically show an "Authorize" button. You can type in your username/password, and Swagger will handle the token exchange and attach it to all your test requests automatically!


Summary

  • OAuth2PasswordBearer: The gatekeeper that extracts your token.
  • JWT: The stateless container for user identity.
  • Encryption: Use python-jose and a strong SECRET_KEY.
  • Dependency Injection: Use get_current_user to make authentication a single, reusable line of code.

In the next lesson, we’ll move from identification to control: Role-Based Access Control (RBAC).


Exercise: The Expiry Guard

Why is it important to have an "exp" (expiry) field in your JWT? What happens if you don't include it?

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn