
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:
- User sends credentials to
/token. - Server verifies credentials.
- Server issues a JWT.
- 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
Authorizationheader. - 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-joseand a strongSECRET_KEY. - Dependency Injection: Use
get_current_userto 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?