FastAPI Authentication Patterns
Purpose: Autonomously implement, validate, and debug FastAPI authentication systems with multiple strategies.
Activation Triggers:
-
Implementing user authentication
-
Securing API endpoints
-
JWT token generation/validation issues
-
OAuth2 flow configuration
-
Permission and role-based access control
-
Supabase authentication integration
-
Authentication errors (401, 403)
-
Password hashing and security
Key Resources:
-
scripts/setup-jwt.sh
-
Initialize JWT authentication system
-
scripts/validate-auth.sh
-
Validate authentication configuration
-
templates/jwt_auth.py
-
Complete JWT authentication implementation
-
templates/oauth2_flow.py
-
OAuth2 password flow with scopes
-
templates/supabase_auth.py
-
Supabase integration for FastAPI
-
examples/protected_routes.py
-
Protected endpoint patterns
-
examples/permission_system.py
-
Role and permission-based access
Authentication Strategies
- JWT Token Authentication
Use When:
-
Need stateless authentication
-
Building API for mobile/web clients
-
Require token expiration control
-
Implementing refresh token patterns
Setup:
./scripts/setup-jwt.sh
Core Components:
-
Password hashing with Argon2 (pwdlib)
-
JWT token generation with expiration
-
Token validation and user extraction
-
Secure secret key management
Implementation Pattern:
Hash passwords (never store plaintext)
password_hash = PasswordHash.recommended() hashed = password_hash.hash(plain_password)
Generate JWT token
def create_access_token(data: dict, expires_delta: timedelta): to_encode = data.copy() expire = datetime.now(timezone.utc) + expires_delta to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")
Validate token and extract user
async def get_current_user(token: str = Depends(oauth2_scheme)): payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) username = payload.get("sub") return get_user(username)
Key Points:
-
Use environment variables for SECRET_KEY
-
Default token expiration: 30 minutes
-
Store username in "sub" claim
-
Validate token signature and expiration
- OAuth2 Password Flow
Use When:
-
Building first-party applications
-
Need username/password authentication
-
Following OAuth2 standards
-
Integrating with OpenAPI documentation
Template: templates/oauth2_flow.py
Flow:
-
User submits credentials via OAuth2PasswordRequestForm
-
Server verifies password hash
-
Server returns signed JWT access token
-
Client includes token in Authorization header
Security Scheme:
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = authenticate_user(form_data.username, form_data.password) access_token = create_access_token(data={"sub": user.username}) return {"access_token": access_token, "token_type": "bearer"}
- OAuth2 Scopes (Permissions)
Use When:
-
Need fine-grained permission control
-
Implementing role-based access
-
Building multi-tenant systems
-
Following least-privilege principle
Template: See templates/oauth2_flow.py (includes scopes)
Define Scopes:
oauth2_scheme = OAuth2PasswordBearer( tokenUrl="token", scopes={ "me": "Read information about current user", "items": "Read items", "items:write": "Create and modify items" } )
Encode Scopes in Token:
During login, add user's scopes to token
token_data = {"sub": username, "scopes": user.scopes} access_token = create_access_token(token_data)
Protect Endpoints with Scopes:
async def get_current_user( security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme) ): # Validate token has required scopes for scope in security_scopes.scopes: if scope not in token_data.scopes: raise HTTPException(401, "Not enough permissions")
@app.get("/users/me/items/") async def read_items( current_user: User = Security(get_current_active_user, scopes=["items"]) ): return current_user.items
- Supabase Authentication
Use When:
-
Using Supabase as backend
-
Need managed authentication service
-
Want OAuth providers (Google, GitHub, etc.)
-
Require user management dashboard
Template: templates/supabase_auth.py
Setup:
from supabase import create_client, Client
supabase: Client = create_client( os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_KEY") )
Sign Up:
Email/password signup
response = supabase.auth.sign_up({ "email": "user@example.com", "password": "secure_password", "options": {"data": {"full_name": "John Doe"}} })
Sign In:
response = supabase.auth.sign_in_with_password({ "email": "user@example.com", "password": "secure_password" }) access_token = response.session.access_token
Validate Token in FastAPI:
async def get_current_user(token: str = Depends(oauth2_scheme)): # Validate Supabase JWT token user = supabase.auth.get_user(token) return user
Integration Pattern:
-
Store Supabase session in HTTP-only cookies (server-side)
-
Use PKCE flow for OAuth providers
-
Implement token refresh logic
-
Leverage Supabase RLS policies for data access
Validation Workflow
- Run Authentication Validator
./scripts/validate-auth.sh
Checks Performed:
-
✅ Required packages installed (fastapi, python-jose[cryptography], passlib[argon2], pwdlib)
-
✅ Environment variables set (SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES)
-
✅ Security scheme configured correctly
-
✅ Password hashing implemented
-
✅ Token generation and validation functions present
-
✅ Protected endpoints use proper dependencies
- Common Issues & Fixes
Missing SECRET_KEY:
Generate secure random key
openssl rand -hex 32
Add to .env
echo 'SECRET_KEY=your_generated_key' >> .env
Token Expired (401):
-
Increase ACCESS_TOKEN_EXPIRE_MINUTES
-
Implement refresh token pattern
-
Check server/client time sync
Invalid Credentials:
-
Verify password hashing algorithm matches
-
Check user exists in database
-
Validate password comparison logic
Missing Permissions (403):
-
Verify user has required scopes
-
Check scope encoding in token
-
Validate SecurityScopes configuration
Supabase Connection Failed:
-
Verify SUPABASE_URL and SUPABASE_KEY
-
Check project settings in Supabase dashboard
-
Validate network connectivity
Protected Routes Pattern
Example: examples/protected_routes.py
Public endpoint (no auth)
@app.get("/") async def root(): return {"message": "Public endpoint"}
Protected endpoint (requires authentication)
@app.get("/users/me") async def read_users_me(current_user: User = Depends(get_current_user)): return current_user
Protected with specific permission
@app.post("/items/") async def create_item( item: Item, current_user: User = Security(get_current_active_user, scopes=["items:write"]) ): return create_item_for_user(current_user, item)
Admin-only endpoint
@app.delete("/users/{user_id}") async def delete_user( user_id: int, current_user: User = Depends(get_current_admin_user) ): return delete_user_by_id(user_id)
Permission System Pattern
Example: examples/permission_system.py
Role-Based Access Control (RBAC):
class Role(str, Enum): ADMIN = "admin" USER = "user" GUEST = "guest"
class User(BaseModel): username: str role: Role permissions: List[str]
def has_permission(user: User, required_permission: str) -> bool: # Admins have all permissions if user.role == Role.ADMIN: return True return required_permission in user.permissions
async def require_permission(permission: str): async def permission_checker(current_user: User = Depends(get_current_user)): if not has_permission(current_user, permission): raise HTTPException(403, f"Permission '{permission}' required") return current_user return permission_checker
Usage
@app.delete("/items/{item_id}") async def delete_item( item_id: int, current_user: User = Depends(require_permission("items:delete")) ): return delete_item_by_id(item_id)
Best Practices
Security:
-
Never store passwords in plaintext
-
Use Argon2 for password hashing (recommended over bcrypt)
-
Store SECRET_KEY in environment variables (never commit)
-
Use HTTPS in production
-
Implement rate limiting on login endpoints
-
Add token refresh mechanism for long sessions
Token Management:
-
Short access token expiration (15-30 minutes)
-
Long refresh token expiration (7-30 days)
-
Rotate refresh tokens on use
-
Implement token revocation list for logout
Scope Design:
-
Use hierarchical scopes (e.g., items, items:read, items:write)
-
Follow least-privilege principle
-
Document all scopes in OpenAPI
-
Validate scopes on every request
Error Handling:
-
Return 401 for authentication failures
-
Return 403 for authorization failures
-
Include WWW-Authenticate header with 401
-
Log authentication attempts for security monitoring
Dependencies
Required Packages:
pip install fastapi pip install python-jose[cryptography] pip install pwdlib[argon2] pip install supabase # If using Supabase
Environment Variables:
SECRET_KEY=your_secret_key_here ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=30
For Supabase
SUPABASE_URL=https://your-project.supabase.co SUPABASE_KEY=your_anon_key
Supported Auth Strategies: JWT, OAuth2 Password Flow, OAuth2 Scopes, Supabase
Version: 1.0.0 FastAPI Compatibility: 0.100+