FastAPI Essentials

Course Content

FastAPI Essentials

1 sections · 32 lessons

Can you explain how you would implement authentication and authorization in FastAPI?


Two dependencies, two different refusalsBearertoken readfrom the headerSignature andexpiry checkedUser loaded fromthe token claimsRole or scopechecked for this routeHandlerruns witha typed UserFails at step 2 give 401; fails at step 4 give 403.
Authentication and authorisation are separate dependencies, so a route can reuse the first and swap the second.

What you need to know

The flow for a user-facing API:

  1. 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.
  2. Call — the client sends Authorization: Bearer <token> on each request.
  3. Authenticate — a dependency verifies the signature and expiry and builds a User.
  4. Authorise — a second dependency checks the user's role or scope for this route.
Python
from typing import Annotatedimport jwtfrom fastapi import Depends, FastAPI, HTTPException, statusfrom fastapi.security import OAuth2PasswordBeareroauth2 = OAuth2PasswordBearer(tokenUrl="token")app = FastAPI()async def current_user(token: Annotated[str, Depends(oauth2)]) -> User:    try:        payload = jwt.decode(token, SECRET, algorithms=["HS256"])    except jwt.PyJWTError:        raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid or expired token",                            headers={"WWW-Authenticate": "Bearer"})    return User(name=payload["sub"], roles=payload.get("roles", []))def require_role(role: str):    async def check(user: Annotated[User, Depends(current_user)]) -> User:        if role not in user.roles:            raise HTTPException(status.HTTP_403_FORBIDDEN, "Not allowed")        return user    return check@app.post("/documents")async def upload_doc(user: Annotated[User, Depends(require_role("editor"))]):    return {"uploaded_by": user.name}

Real responses from TestClient:

Text
no header                      -> 401 {'detail': 'Not authenticated'}"Bearer junk"                  -> 401valid token, role "viewer"     -> 403valid token, role "editor"     -> 200 {'uploaded_by': 'asha'}expired token                  -> 401

jwt.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 SECRET from 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 with hmac.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 User to the handler, appears as a security scheme in /docs, and can be replaced in tests with dependency_overrides.
  • "What is the difference between Depends and Security?" — Security is Depends plus OAuth2 scopes, which FastAPI also records in the OpenAPI document.