FastAPI Authentication, Security Schemes & OpenAPI
FastAPI provides native support for authentication, security schemes, and automatic OpenAPI documentation generation. Built-in security utilities (OAuth2PasswordBearer, HTTPBearer, APIKeyHeader) integrate directly with FastAPI’s Dependency Injection engine. When a security dependency is injected into a route, it extracts security credentials, validates tokens, enforces scope permissions, and automatically updates the interactive Swagger UI (/docs).
This chapter details FastAPI’s security dependency architecture, JWT authentication pipelines, scope permission enforcement, and custom OpenAPI schema generation.
1. Security Dependency Architecture (OAuth2PasswordBearer)
FastAPI’s security models inherit from fastapi.security.base.SecurityBase.
When you instantiate OAuth2PasswordBearer(tokenUrl="token"):
- OpenAPI Registration: It registers an
OAuth2security scheme in the generated OpenAPI schema (components.securitySchemes). - Credential Extraction: It acts as a dependency that extracts the
Authorization: Bearer <token>string from the HTTP request header. - Automatic 401 Error: If the
Authorizationheader is missing or malformed, FastAPI automatically raises anHTTP_401_UNAUTHORIZEDexception.
FastAPI Security Dependency Pipeline:
[ Client HTTP Request (Header: Authorization: Bearer <jwt>) ]
|
v
[ OAuth2PasswordBearer Dependency (Extracts token string) ]
|
v
[ get_current_user Dependency (Validates JWT signature & expiry) ]
|
v
[ Security(get_current_user, scopes=["admin:write"]) ]
|
+---> Check Token Scopes vs Required Scopes
|
v
[ Route Handler Executed (Injects Authenticated User) ]2. JWT Verification & Scope Enforcement (Security())
For fine-grained permission control (Role-Based Access Control / Scope-Based Access Control), FastAPI supplies Security():
Depends()vsSecurity():Depends()resolves standard dependencies.Security()resolves dependencies while also passing a list of required OpenAPI Scopes (scopes=["items:read", "items:write"]).
from fastapi import Security, HTTPException, status
from fastapi.security import SecurityScopes, OAuth2PasswordBearer
import jwt
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", scopes={"items:read": "Read items"})
async def get_current_user(security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)):
if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else:
authenticate_value = "Bearer"
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
token_scopes = payload.get("scopes", [])
except jwt.PyJWTError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, headers={"WWW-Authenticate": authenticate_value})
# Validate required scopes
for scope in security_scopes.scopes:
if scope not in token_scopes:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
headers={"WWW-Authenticate": authenticate_value},
)
return payload3. Dynamic OpenAPI Customization (get_openapi())
FastAPI dynamically builds the OpenAPI 3.0 schema by inspecting Pydantic models and route dependencies. You can override app.openapi() to inject custom security schemes, API branding, or server URLs:
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Enterprise Microservice API",
version="2.0.0",
description="High-performance async API",
routes=app.routes,
)
# Custom security scheme injection
openapi_schema["info"]["x-logo"] = {"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"}
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi4. Production Trade-offs & JWT Security
- Asymmetric JWT Signing (RS256/ES256): Use asymmetric key pairs (Public/Private key) in production microservices. The Auth service signs JWTs with the Private Key; downstream microservices verify signatures using the Public Key without needing access to secret keys.
- Token Invalidation: JWTs are stateless. Implement a Redis-backed token revocation list (JTI revocation) or keep access token expiration short (e.g. 15 minutes) combined with sliding refresh tokens.