Course Content
FastAPI Essentials
1 sections · 32 lessons
Can you explain how you would implement authentication and authorization in FastAPI?
What you need to know
The flow for a user-facing API:
- Log in — the client posts username and password to
/token; the server checks the password hash and returns a JWT that expires in, say, 15 minutes. - Call — the client sends
Authorization: Bearer <token>on each request. - Authenticate — a dependency verifies the signature and expiry and builds a
User. - Authorise — a second dependency checks the user's role or scope for this route.
1from typing import Annotated2import jwt3from fastapi import Depends, FastAPI, HTTPException, status4from fastapi.security import OAuth2PasswordBearer56oauth2 = OAuth2PasswordBearer(tokenUrl="token")7app = FastAPI()89async def current_user(token: Annotated[str, Depends(oauth2)]) -> User:10 try:11 payload = jwt.decode(token, SECRET, algorithms=["HS256"])12 except jwt.PyJWTError:13 raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid or expired token",14 headers={"WWW-Authenticate": "Bearer"})15 return User(name=payload["sub"], roles=payload.get("roles", []))1617def require_role(role: str):18 async def check(user: Annotated[User, Depends(current_user)]) -> User:19 if role not in user.roles:20 raise HTTPException(status.HTTP_403_FORBIDDEN, "Not allowed")21 return user22 return check2324@app.post("/documents")25async def upload_doc(user: Annotated[User, Depends(require_role("editor"))]):26 return {"uploaded_by": user.name}Real responses from TestClient:
no header -> 401 {'detail': 'Not authenticated'}"Bearer junk" -> 401valid token, role "viewer" -> 403valid token, role "editor" -> 200 {'uploaded_by': 'asha'}expired token -> 401jwt.decode (from PyJWT) checks the signature and the exp claim together. require_role is a dependency factory: it returns a new dependency for each role, and that dependency itself depends on current_user. To protect a whole area at once, pass it to a router: APIRouter(dependencies=[Depends(require_role("admin"))]).
Other points interviewers check
- Passwords: store only a slow hash (Argon2 or bcrypt, for example with
pwdlib), never the password. - Secrets: load
SECRETfrom the environment or a secret manager. Short token expiry plus refresh tokens limits the damage of a leak. - API keys for services: use
APIKeyHeader(name="X-API-Key"), store a SHA-256 hash of each key, and compare withhmac.compare_digest. The key also identifies the tenant for quotas and billing. - Real user login is often delegated to an identity provider (Keycloak, Auth0, Cognito); FastAPI then only verifies their JWTs.
A real-life example
An internal RAG assistant has three kinds of user. Viewers can ask questions, editors can upload documents to the index, and admins can delete the whole index. Each route declares its need: require_role("viewer") on /ask, require_role("editor") on /documents, and the admin router carries require_role("admin") for every route inside it.
A partner company also calls the /ask endpoint from its own backend. It gets an API key instead of user logins. Each key maps to a tenant id, which the rate limiter and the token-usage report both use. When a contractor leaves, their user is disabled at the identity provider; their JWT stops working within 15 minutes, when it expires.
Follow-up questions to expect
- "How do you revoke a JWT before it expires?" — Keep expiry short and use refresh tokens, or add a
jti(token id) claim and check it against a deny-list in Redis. - "Why a dependency and not middleware?" — A dependency runs per route, returns a typed
Userto the handler, appears as a security scheme in/docs, and can be replaced in tests withdependency_overrides. - "What is the difference between
DependsandSecurity?" —SecurityisDependsplus OAuth2 scopes, which FastAPI also records in the OpenAPI document.