Security Guide
Table of contents
- Overview
- Authentication Schemes
- OIDC Authentication
- Security Headers
- User Model
- JWT Token Validation
- Swagger UI Integration
- Rate Limiting
- Best Practices
- Example: Complete Secure Application
- Troubleshooting
- Next Steps
Overview
The security module provides OIDC/OAuth2 authentication and authorization utilities for FastAPI applications.
Authentication Schemes
The library provides two authentication schemes for different use cases, both of which are automatically available in Swagger UI:
- AuthToken (OAuth2 Flow): A standard OAuth2 Authorization Code flow with PKCE. This is recommended for production use and is the default for automated frontend integrations.
- IDToken (Bearer): A standard HTTP Bearer scheme where you can manually paste a JWT (ID Token). This is useful for manual testing in Swagger UI or when you already have a token from another source.
Swagger UI Integration
The create_app() function automatically configures both schemes. In Swagger UI, you will see two authorization options:
- AuthToken: Uses the
OIDC_AUTH_URLandOIDC_TOKEN_URLto perform a full OAuth2 flow. - IDToken: Allows you to manually enter “Bearer
".
Both schemes are verified using the same OIDC configuration.
OIDC Authentication
Configuration
Configure your OIDC provider using environment variables:
# SSL Certificate Verification (default: True)
SSL_VERIFY=True # Set to False for self-signed certificates in development
# Automatic configuration (recommended)
OIDC_DISCOVERY_URL=https://auth.example.com/.well-known/openid-configuration
# Manual configuration
OIDC_ISSUER=https://auth.example.com/realms/organization
OIDC_JWKS_URI=https://auth.example.com/realms/organization/protocol/openid-connect/certs
OIDC_TOKEN_URL=https://auth.example.com/realms/organization/protocol/openid-connect/token
OIDC_AUTH_URL=https://auth.example.com/realms/organization/protocol/openid-connect/auth
# Client configuration
OIDC_CLIENT_ID=my-client-id
OIDC_AUDIENCE=account
SWAGGER_CLIENT_ID=my-swagger-client-id
# Token configuration
TOKEN_ALGORITHMS=RS256
OIDC_USER_NAME_CLAIM=preferred_username
OIDC_USER_ID_CLAIM=sub
Using Authentication Dependencies
Strict Authentication
Requires valid JWT token, returns 401 if missing or invalid:
from fastapi import Depends, FastAPI
from fastapi_otel_common.security import get_current_user
from fastapi_otel_common.core.models import UserBase
app = FastAPI()
@app.get("/protected")
async def protected_route(user: UserBase = Depends(get_current_user)):
return {
"message": "Access granted",
"user_id": user.id,
"email": user.email,
"name": f"{user.given_name} {user.family_name}"
}
Optional Authentication
Returns None if token is missing or invalid:
from typing import Optional
from fastapi import Depends
from fastapi_otel_common.security import get_current_user_optional
from fastapi_otel_common.core.models import UserBase
@app.get("/public")
async def public_route(user: Optional[UserBase] = Depends(get_current_user_optional)):
if user:
return {"message": f"Hello, {user.given_name}!"}
return {"message": "Hello, anonymous!"}
Security Headers
The security headers middleware adds OWASP-recommended headers to all responses:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; ...
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
Customizing Security Headers
To customize security headers, you can modify the middleware or override specific endpoints:
from fastapi import Response
@app.get("/custom")
async def custom_headers(response: Response):
response.headers["X-Custom-Header"] = "value"
return {"message": "Custom headers"}
User Model
The UserBase model represents authenticated user information:
from fastapi_otel_common.core.models import UserBase
class UserBase(BaseModel):
id: str # User ID from OIDC provider
email: str # User email
given_name: str # First name
family_name: Optional[str] # Last name
is_admin: bool = False # Admin flag
roles: Dict[str, List[str]] # Client ID to roles mapping
Role-Based Access Control (RBAC)
The library provides an RBAC system based on client roles and realm roles extracted from the JWT token.
Basic Role Requirements
Use RequireRoles for “OR” logic (at least one role required) and RequireAllRoles for “AND” logic (all roles required):
from fastapi import Depends
from fastapi_otel_common.security import RequireRoles, RequireAllRoles
# Requires 'admin' OR 'manager'
@app.get("/admin", dependencies=[Depends(RequireRoles(["admin", "manager"]))])
async def admin_area():
return {"message": "Welcome"}
# Requires BOTH 'admin' AND 'auditor'
@app.get("/super-secret", dependencies=[Depends(RequireAllRoles(["admin", "auditor"]))])
async def secret_area():
return {"message": "Welcome, super admin"}
Complex RBAC Logic
The library supports complex boolean logic (AND/OR/Nested) using RequireRolesComplex:
from fastapi_otel_common.security import (
RequireRolesComplex, AnyRole, AllRoles, AnyCondition, AllConditions
)
complex_condition = AnyCondition(
AllRoles(["admin", "auditor"]),
AnyRole(["superadmin"])
)
@app.delete("/sensitive-data")
async def delete_data(user = Depends(RequireRolesComplex(complex_condition))):
return {"status": "deleted"}
For more details, see the Role-Based Access Control guide.
JWT Token Validation
The library automatically validates JWT tokens:
- Fetches JWKS from the OIDC provider
- Validates token signature
- Checks issuer and audience
- Extracts user claims
- Returns structured user information
Supported OIDC Providers
- Keycloak
- Auth0
- Okta
- Azure AD
- Any standard OIDC-compliant provider
Swagger UI Integration
The create_app() function automatically configures Swagger UI for OIDC:
from fastapi_otel_common import create_app
app = create_app(
title="My API",
swagger_ui_init_oauth={
"usePkceWithAuthorizationCodeGrant": True,
"clientId": "my-swagger-client-id",
"scopes": "openid profile email api:read api:write"
}
)
Access Swagger UI at /docs and authenticate using the “Authorize” button.
Rate Limiting
Protect your API from abuse with rate limiting:
ENABLE_RATE_LIMIT_MIDDLEWARE=True
RATE_LIMIT_PER_MINUTE=60
RATE_LIMIT_PER_HOUR=1000
Rate limiting is applied per IP address. Health check endpoints (/health, /docs) are excluded.
Custom Rate Limits
Apply custom limits to specific routes:
from fastapi_otel_common import create_app
app = create_app()
limiter = app.state.limiter
@app.get("/expensive")
@limiter.limit("5/minute")
async def expensive_endpoint(request: Request):
# Heavy operation
return {"status": "ok"}
Best Practices
- Use HTTPS in production - Never send tokens over HTTP
- Always verify SSL certificates - Keep
SSL_VERIFY=Truein production - Rotate secrets regularly - Update client secrets periodically
- Limit token scope - Request only necessary scopes
- Enable rate limiting - Protect against abuse
- Monitor authentication - Track failed login attempts
- Use short-lived tokens - Configure appropriate token lifetimes
- Validate audience - Ensure tokens are intended for your service
Example: Complete Secure Application
from fastapi import Depends, FastAPI
from fastapi_otel_common import create_app
from fastapi_otel_common.security import get_current_user
from fastapi_otel_common.core.models import UserBase
# Create app with security and automatic OpenTelemetry instrumentation
app = create_app(
title="Secure API",
version="1.0.0"
)
@app.get("/")
async def root():
return {"message": "Public endpoint"}
@app.get("/profile")
async def get_profile(user: UserBase = Depends(get_current_user)):
return {
"user_id": user.id,
"email": user.email,
"name": f"{user.given_name} {user.family_name}"
}
@app.get("/admin")
async def admin_only(user: UserBase = Depends(get_current_user)):
if not user.is_admin:
raise HTTPException(status_code=403, detail="Admin access required")
return {"message": "Admin access granted"}
Troubleshooting
Token Validation Fails
- Check OIDC discovery URL is accessible
- Verify
OIDC_AUDIENCEmatches your client - Ensure
TOKEN_ALGORITHMSis correct (usually RS256) - Check token hasn’t expired
Swagger UI Authorization Not Working
- Verify
SWAGGER_CLIENT_IDis set correctly - Check redirect URL is allowed in OIDC provider
- Ensure scopes are configured properly
Next Steps
- Database Guide - Set up database integration
- Examples - See complete examples
- API Reference - Full API documentation