A modern, flexible authentication system for FastAPI applications with support for multiple authentication methods and strategies.
- Features
- Installation
- Core Concepts
- Basic Usage Guide
- Advanced Usage Guide
- Security Best Practices
- Troubleshooting
- Contributing
- License
Warning
FastSecure is still experimental.
-
🔐 Multiple Authentication Methods
- JWT tokens with refresh token support and flexible configuration
- Session-based authentication with configurable storage backends
- OAuth 2.0 providers (Google, GitHub) with standardized user info
- Easy to extend with custom authentication providers
-
🔄 Flexible Authentication Strategies
- Use single or multiple authentication methods
- Support for AND logic (require all methods)
- Support for OR logic (allow any method)
- Optional authentication methods
- Path-based authentication requirements
-
🗄️ Session Storage Options
- In-memory storage for development
- Redis backend for distributed systems
- Database backend (SQLAlchemy) for persistence
- Easy to implement custom storage backends
-
🛡️ Security Features
- Token expiration and refresh
- Session timeout and cleanup
- Concurrent session limits
- Scope-based authorization
- IP tracking and user agent logging
- Install using pip:
pip install fastsecure
- Install using uv or poetry:
uv add fastsecure
poetry add fastsecure
Before diving into the implementation, let's understand the core concepts:
The AuthenticationManager
is the central component that:
- Manages authentication providers
- Handles authentication strategies
- Enforces authentication requirements
- Coordinates the authentication flow
Providers implement specific authentication methods:
- JWT tokens
- Sessions
- OAuth
- Custom methods
Strategies determine how multiple authentication methods are combined:
AuthStrategy.ANY
: Any provider can authenticate (OR logic)AuthStrategy.ALL
: All providers must authenticate (AND logic)
Requirements specify which authentication methods are needed for specific paths:
- Required providers
- Optional providers
- Authentication strategy
- Required scopes
- Create a Basic FastAPI Application
from fastapi import FastAPI, Depends, HTTPException
from fastsecure import AuthenticationManager, JWTAuthenticationProvider
app = FastAPI()
- Initialize Authentication Manager and JWT Provider
# Initialize authentication
auth_manager = AuthenticationManager()
# Configure JWT provider
jwt_auth = JWTAuthenticationProvider(
secret_key="your-secret-key", # Use a secure key in production!
access_token_expire_minutes=30,
refresh_token_expire_days=7
)
# Register the provider
auth_manager.register_provider("jwt", jwt_auth)
- Create Authentication Dependency
from fastapi import Request
from typing import Optional
async def requires_auth(request: Request, path: Optional[str] = None) -> dict:
"""
FastAPI dependency for authentication requirements.
Args:
request: The FastAPI request object
path: Optional path override for authentication requirements
Returns:
Authentication result if successful
Raises:
HTTPException: If authentication fails
"""
# Get authentication data from request headers
auth_header = request.headers.get("Authorization")
if not auth_header:
raise HTTPException(status_code=401, detail="No authentication provided")
# Parse Bearer token
try:
scheme, token = auth_header.split()
if scheme.lower() != "bearer":
raise HTTPException(status_code=401, detail="Invalid authentication scheme")
except ValueError:
raise HTTPException(status_code=401, detail="Invalid authorization header")
# Authenticate using the manager
result = await auth_manager.authenticate(
path or request.url.path,
{"jwt": {"access_token": token}}
)
if not result.success:
raise HTTPException(
status_code=401,
detail="Authentication failed",
headers={"WWW-Authenticate": "Bearer"}
)
return result
- Configure Protected Routes
# Add authentication requirement for protected paths
auth_manager.add_requirement(
"/api/protected/*", # Path pattern
providers=["jwt"], # Required providers
scopes=["read"] # Required scopes (optional)
)
- Implement Login and Protected Routes
from pydantic import BaseModel
class LoginCredentials(BaseModel):
username: str
password: str
@app.post("/api/auth/login")
async def login(credentials: LoginCredentials):
# Validate credentials (implement your own validation)
user_id = validate_credentials(credentials)
# Authenticate with JWT provider
result = await auth_manager.authenticate(
"/api/protected/data",
{"jwt": {
"user_id": user_id,
"scopes": ["read", "write"]
}}
)
if not result.success:
raise HTTPException(401, "Authentication failed")
return {
"access_token": result.access_token,
"refresh_token": result.refresh_token,
"token_type": "Bearer",
"expires_at": result.expires_at
}
@app.get("/api/protected/data")
async def protected_data(auth = Depends(requires_auth)):
return {
"message": "Authenticated!",
"user_id": auth.user_id,
"scopes": auth.metadata.get("scopes", [])
}
- Choose a Storage Backend
from fastsecure import (
SessionAuthenticationProvider,
RedisSessionStore,
DatabaseSessionStore
)
# For development (in-memory)
session_auth = SessionAuthenticationProvider()
# For production with Redis
session_auth = SessionAuthenticationProvider(
session_store=RedisSessionStore("redis://localhost"),
session_timeout_minutes=60,
max_sessions_per_user=3,
cleanup_expired=True
)
- Configure Session Authentication
# Register the provider
auth_manager.register_provider("session", session_auth)
# Add requirements
auth_manager.add_requirement(
"/api/user/*",
providers=["session"]
)
- Implement Session Login
@app.post("/api/auth/session/login")
async def session_login(
credentials: LoginCredentials,
request: Request
):
user_id = validate_credentials(credentials)
result = await auth_manager.authenticate(
"/api/user/profile",
{"session": {
"user_id": user_id,
"ip_address": request.client.host,
"user_agent": request.headers.get("user-agent"),
"scopes": ["user:read"]
}}
)
if not result.success:
raise HTTPException(401, "Authentication failed")
response = JSONResponse({
"message": "Logged in successfully",
"user_id": user_id
})
# Set session cookie
response.set_cookie(
"session_id",
result.session_id,
httponly=True,
secure=True,
samesite="lax",
expires=result.expires_at
)
return response
- Configure OAuth Providers
from fastsecure import GoogleAuthProvider, GitHubAuthProvider
# Google Sign-In
google_auth = GoogleAuthProvider(
client_id="your-client-id",
client_secret="your-client-secret",
redirect_uri="http://localhost:8000/auth/google/callback"
)
# GitHub Sign-In
github_auth = GitHubAuthProvider(
client_id="your-client-id",
client_secret="your-client-secret",
redirect_uri="http://localhost:8000/auth/github/callback"
)
# Register providers
auth_manager.register_provider("google", google_auth)
auth_manager.register_provider("github", github_auth)
- Implement OAuth Flow
@app.get("/auth/google/login")
async def google_login():
authorization_url = google_auth.get_authorization_url(
state="random-secure-state" # Implement secure state handling
)
return RedirectResponse(authorization_url)
@app.get("/auth/google/callback")
async def google_callback(code: str, state: str):
# Validate state
validate_oauth_state(state)
result = await auth_manager.authenticate(
"/api/user/profile",
{"google": {"code": code}}
)
if not result.success:
raise HTTPException(401, "Authentication failed")
# Get user info from result
user_info = result.metadata["user_info"]
return {
"message": "Logged in with Google",
"email": user_info["email"],
"name": user_info["name"],
"access_token": result.access_token
}
- Require Multiple Methods (AND Strategy)
# Require both JWT and session authentication
auth_manager.add_requirement(
"/api/admin/*",
providers=["jwt", "session"],
strategy=AuthStrategy.ALL,
scopes=["admin"]
)
# Example request handler
@app.post("/api/admin/action")
async def admin_action(auth = Depends(auth_manager.requires_auth)):
if "admin" not in auth.metadata.get("scopes", []):
raise HTTPException(403, "Insufficient permissions")
return {"message": "Admin action successful"}
- Allow Alternative Methods (OR Strategy)
# Allow any authentication method
auth_manager.add_requirement(
"/api/user/*",
providers=["jwt", "session", "google"],
strategy=AuthStrategy.ANY
)
- Optional Authentication
# Add optional authentication methods
auth_manager.add_requirement(
"/api/public/*",
providers=["jwt"],
optional_providers=["session", "google"]
)
- Create a Custom Provider
from fastsecure import AuthenticationProvider, AuthenticationResult
from typing import Dict, Any, Set
class APIKeyAuthProvider(AuthenticationProvider):
def __init__(self, api_keys: Dict[str, str]):
self.api_keys = api_keys
def get_required_credentials(self) -> Set[str]:
return {"api_key"}
async def authenticate(
self,
credentials: Dict[str, Any]
) -> AuthenticationResult:
api_key = credentials.get("api_key")
if api_key not in self.api_keys:
return AuthenticationResult(
success=False,
provider=self.provider_name,
metadata={"error": "Invalid API key"}
)
return AuthenticationResult(
success=True,
provider=self.provider_name,
user_id=self.api_keys[api_key],
metadata={"api_key": api_key}
)
async def validate_authentication(
self,
auth_data: Dict[str, Any]
) -> bool:
return auth_data.get("api_key") in self.api_keys
- Register and Use Custom Provider
# Initialize with API keys
api_key_auth = APIKeyAuthProvider({
"key1": "user1",
"key2": "user2"
})
# Register provider
auth_manager.register_provider("apikey", api_key_auth)
# Add requirement
auth_manager.add_requirement(
"/api/service/*",
providers=["apikey"]
)
- Implement Custom Session Storage
from fastsecure import SessionStore
from typing import Dict, Any, List, Optional
from datetime import datetime
class CustomSessionStore(SessionStore):
async def create_session(
self,
user_id: int,
session_id: str,
expires_at: datetime,
metadata: Dict[str, Any]
) -> bool:
# Implement session creation
pass
async def get_session(
self,
session_id: str
) -> Optional[Dict[str, Any]]:
# Implement session retrieval
pass
async def update_session(
self,
session_id: str,
metadata: Dict[str, Any]
) -> bool:
# Implement session update
pass
async def delete_session(
self,
session_id: str
) -> bool:
# Implement session deletion
pass
async def get_user_sessions(
self,
user_id: int
) -> List[Dict[str, Any]]:
# Implement user sessions retrieval
pass
-
JWT Security
- Use strong secret keys
- Set appropriate token expiration times
- Implement token refresh securely
- Use HTTPS for token transmission
-
Session Security
- Enable secure cookie attributes
- Implement session timeout
- Limit concurrent sessions
- Clean up expired sessions
-
OAuth Security
- Validate OAuth state parameter
- Use HTTPS for callbacks
- Validate token scopes
- Handle user data securely
-
General Security
- Implement rate limiting
- Use secure password handling
- Log security events
- Regular security audits
This project is licensed under the MIT License - see the LICENSE file for details.
Igor Benav – @igorbenav – igormagalhaesr@gmail.com github.com/igorbenav