python 56 lines · 8 steps

Chaining auth dependencies in FastAPI

A layered chain of FastAPI dependencies decodes a JWT, loads the user, enforces an admin role, then guards a delete endpoint.

Explained by highlit
1from fastapi import APIRouter, Depends, HTTPException, status
2from fastapi.security import OAuth2PasswordBearer
3from jose import JWTError, jwt
4from sqlalchemy.orm import Session
5 
6from .database import get_db
7from .models import User
8from .config import settings
9 
10router = APIRouter(prefix="/admin", tags=["admin"])
11oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
12 
13 
14def get_current_user(
15 token: str = Depends(oauth2_scheme),
16 db: Session = Depends(get_db),
17) -> User:
18 credentials_error = HTTPException(
19 status_code=status.HTTP_401_UNAUTHORIZED,
20 detail="Could not validate credentials",
21 headers={"WWW-Authenticate": "Bearer"},
22 )
23 try:
24 payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
25 user_id = payload.get("sub")
26 if user_id is None:
27 raise credentials_error
28 except JWTError:
29 raise credentials_error
30 
31 user = db.query(User).filter(User.id == int(user_id)).first()
32 if user is None:
33 raise credentials_error
34 return user
35 
36 
37def require_admin(current_user: User = Depends(get_current_user)) -> User:
38 if current_user.role != "admin":
39 raise HTTPException(
40 status_code=status.HTTP_403_FORBIDDEN,
41 detail="Admin privileges required",
42 )
43 return current_user
44 
45 
46@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
47def delete_user(
48 user_id: int,
49 admin: User = Depends(require_admin),
50 db: Session = Depends(get_db),
51) -> None:
52 target = db.query(User).filter(User.id == user_id).first()
53 if target is None:
54 raise HTTPException(status_code=404, detail="User not found")
55 db.delete(target)
56 db.commit()
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1FastAPI dependencies compose: one dependency can depend on another to build layered access checks.
  2. 2Decode and validate a JWT once in a reusable dependency, then reuse it everywhere via Depends.
  3. 3Return the same generic 401 for every failure mode so attackers can't distinguish causes.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Chaining auth dependencies in FastAPI — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code