Skip to main content

Flexible authentication system for FastAPI applications

Project description

FastSecure

A modern, flexible authentication system for FastAPI applications with support for multiple authentication methods and strategies.

Table of Contents

Features

  • 🔐 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

Installation

  1. Install using pip:
pip install fastsecure
  1. Install optional dependencies for specific features:
# Redis storage backend
pip install fastsecure[redis]

# Database storage backend
pip install fastsecure[database]

# All optional dependencies
pip install fastsecure[all]

Core Concepts

Before diving into the implementation, let's understand the core concepts:

Authentication Manager

The AuthenticationManager is the central component that:

  • Manages authentication providers
  • Handles authentication strategies
  • Enforces authentication requirements
  • Coordinates the authentication flow

Authentication Providers

Providers implement specific authentication methods:

  • JWT tokens
  • Sessions
  • OAuth
  • Custom methods

Authentication Strategies

Strategies determine how multiple authentication methods are combined:

  • AuthStrategy.ANY: Any provider can authenticate (OR logic)
  • AuthStrategy.ALL: All providers must authenticate (AND logic)

Authentication Requirements

Requirements specify which authentication methods are needed for specific paths:

  • Required providers
  • Optional providers
  • Authentication strategy
  • Required scopes

Basic Usage Guide

Setting Up JWT Authentication

  1. Create a Basic FastAPI Application
from fastapi import FastAPI, Depends, HTTPException
from fastsecure import AuthenticationManager, JWTAuthenticationProvider

app = FastAPI()
  1. 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)
  1. 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)
)
  1. 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(auth_manager.requires_auth)):
    return {
        "message": "Authenticated!",
        "user_id": auth.user_id,
        "scopes": auth.metadata.get("scopes", [])
    }
  1. Implement Token Refresh
@app.post("/api/auth/refresh")
async def refresh_token(refresh_token: str):
    result = await auth_manager.refresh_authentication(
        "jwt",
        {"refresh_token": refresh_token}
    )
    
    if not result.success:
        raise HTTPException(401, "Token refresh failed")
    
    return {
        "access_token": result.access_token,
        "refresh_token": result.refresh_token,
        "token_type": "Bearer",
        "expires_at": result.expires_at
    }

Setting Up Session Authentication

  1. 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
)
  1. Configure Session Authentication
# Register the provider
auth_manager.register_provider("session", session_auth)

# Add requirements
auth_manager.add_requirement(
    "/api/user/*",
    providers=["session"]
)
  1. 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

Setting Up OAuth Authentication

  1. 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)
  1. 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
    }

Advanced Usage Guide

Combining Authentication Methods

  1. 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"}
  1. Allow Alternative Methods (OR Strategy)
# Allow any authentication method
auth_manager.add_requirement(
    "/api/user/*",
    providers=["jwt", "session", "google"],
    strategy=AuthStrategy.ANY
)
  1. Optional Authentication
# Add optional authentication methods
auth_manager.add_requirement(
    "/api/public/*",
    providers=["jwt"],
    optional_providers=["session", "google"]
)

Custom Authentication Providers

  1. 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
  1. 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"]
)

Storage Backends

  1. 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

Security Best Practices

  1. JWT Security

    • Use strong secret keys
    • Set appropriate token expiration times
    • Implement token refresh securely
    • Use HTTPS for token transmission
  2. Session Security

    • Enable secure cookie attributes
    • Implement session timeout
    • Limit concurrent sessions
    • Clean up expired sessions
  3. OAuth Security

    • Validate OAuth state parameter
    • Use HTTPS for callbacks
    • Validate token scopes
    • Handle user data securely
  4. General Security

    • Implement rate limiting
    • Use secure password handling
    • Log security events
    • Regular security audits

Troubleshooting

Common issues and solutions:

  1. Token Validation Fails

    • Check token expiration
    • Verify secret keys match
    • Ensure correct token format
  2. Session Issues

    • Verify storage backend connection
    • Check session timeout settings
    • Validate cookie settings
  3. OAuth Problems

    • Confirm OAuth credentials
    • Check redirect URI configuration
    • Verify state parameter handling

Contributing

We welcome contributions! Here's how you can help:

  1. Code Contributions

    • Fork the repository
    • Create a feature branch
    • Submit a pull request
  2. Bug Reports

    • Use the issue tracker
    • Provide reproduction steps
    • Include relevant logs
  3. Documentation

    • Improve examples
    • Fix typos
    • Add tutorials
  4. Feature Requests

    • Describe the feature
    • Explain use cases
    • Provide examples

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastsecure-0.2.0.tar.gz (43.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastsecure-0.2.0-py3-none-any.whl (40.0 kB view details)

Uploaded Python 3

File details

Details for the file fastsecure-0.2.0.tar.gz.

File metadata

  • Download URL: fastsecure-0.2.0.tar.gz
  • Upload date:
  • Size: 43.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.4.18

File hashes

Hashes for fastsecure-0.2.0.tar.gz
Algorithm Hash digest
SHA256 9499ee6ecd868b7e7cefe714ee26a2e0a42d619cda44c253c53561593c35b2c6
MD5 315d0544779d981819382c4e84066a7e
BLAKE2b-256 3630b6c3ec51a23af5a88020ccbc3a2a9317bfe42ce33c12a96552b1bbb10a53

See more details on using hashes here.

File details

Details for the file fastsecure-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: fastsecure-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 40.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.4.18

File hashes

Hashes for fastsecure-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2c655f251cee776ac425f9ed55eb23a72787319a7daeb626dd4b2d04ee1b76aa
MD5 fd9e90352934f805444e0555de6d7d36
BLAKE2b-256 274fdf94c1dfd98292bea9eb59e003ae7b9ed314a08e87c8e13aa1a388a66974

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page