Middleware Configuration

This section covers advanced configuration options for Auth Middleware, including environment variables, custom providers, and performance tuning.

Basic Middleware Setup

The Auth Middleware is added to your FastAPI or Starlette application using the standard middleware protocol:

from fastapi import FastAPI
from auth_middleware import JwtAuthMiddleware
from auth_middleware.providers.authn.cognito_provider import CognitoProvider

app = FastAPI()

# Add authentication middleware
app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=your_auth_provider,
    # Additional configuration options...
)

Middleware Parameters

The JwtAuthMiddleware accepts the following parameters:

Parameter

Description

Type

Default

auth_provider

The authentication provider instance

AuthProvider

Required

exclude_paths

Paths to exclude from authentication

List[str]

[]

include_paths

Paths to include in authentication

List[str]

["/*"]

auth_header_name

HTTP header for authentication token

str

"Authorization"

auth_scheme

Authentication scheme

str

"Bearer"

Path Configuration

Excluding Paths from Authentication

You can exclude specific paths from authentication requirements:

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=cognito_provider,
    exclude_paths=[
        "/",                    # Public homepage
        "/health",              # Health check endpoint
        "/docs",                # OpenAPI documentation
        "/openapi.json",        # OpenAPI schema
        "/metrics",             # Prometheus metrics
        "/static/*",            # Static files
        "/public/*",            # Public assets
    ]
)

Including Specific Paths Only

Alternatively, you can specify only the paths that require authentication:

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=cognito_provider,
    include_paths=[
        "/api/*",               # All API endpoints
        "/admin/*",             # Admin interface
        "/user/*",              # User-specific endpoints
    ]
)

Pattern Matching

The middleware supports glob-style pattern matching:

  • * matches any characters within a single path segment

  • ** matches any characters across multiple path segments

  • ? matches any single character

exclude_paths = [
    "/api/v*/public/*",     # Version-specific public endpoints
    "/health*",             # All health-related endpoints
    "/static/**",           # All static files recursively
]

Environment Variables

Core Configuration

Environment Variable

Description

Default

AUTH_MIDDLEWARE_DISABLED

Disable all authentication

false

AUTH_MIDDLEWARE_LOG_LEVEL

Logging level

INFO

AUTH_MIDDLEWARE_LOGGER_NAME

Logger name

auth_middleware

Provider-Specific Variables

AWS Cognito:

AWS_COGNITO_USER_POOL_ID=us-east-1_abcdef123
AWS_COGNITO_USER_POOL_REGION=us-east-1
TOKEN_VERIFICATION_DISABLED=false

Azure Entra ID:

AZURE_TENANT_ID=your-tenant-id
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret

JWT Provider:

JWT_SECRET_KEY=your-secret-key
JWT_ALGORITHM=HS256
JWT_ISSUER=your-issuer

Custom Authentication Providers

Creating a Custom Provider

You can create custom authentication providers by implementing the AuthProvider interface:

from abc import ABC, abstractmethod
from typing import Optional
from auth_middleware.types import User

class CustomAuthProvider(ABC):
    @abstractmethod
    async def authenticate(self, token: str) -> Optional[User]:
        """Validate token and return user information."""
        pass

    @abstractmethod
    async def get_user_groups(self, user: User) -> List[str]:
        """Get user group memberships."""
        pass

Example Custom Provider

import jwt
from auth_middleware.types import User
from auth_middleware.exceptions import AuthenticationError

class ApiKeyProvider:
    def __init__(self, valid_api_keys: dict):
        self.valid_api_keys = valid_api_keys

    async def authenticate(self, token: str) -> Optional[User]:
        # Remove 'Bearer ' prefix if present
        if token.startswith('Bearer '):
            token = token[7:]

        # Look up API key
        user_info = self.valid_api_keys.get(token)
        if not user_info:
            raise AuthenticationError("Invalid API key")

        return User(
            id=user_info["id"],
            name=user_info["name"],
            email=user_info["email"],
            groups=user_info.get("groups", []),
            raw_token=token,
        )

    async def get_user_groups(self, user: User) -> List[str]:
        return user.groups

# Usage
api_keys = {
    "api_key_123": {
        "id": "user1",
        "name": "John Doe",
        "email": "john@example.com",
        "groups": ["user", "admin"]
    }
}

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=ApiKeyProvider(api_keys)
)

Performance Configuration

Connection Pooling

For providers that make HTTP requests (like Cognito), configure connection pooling:

import httpx
from auth_middleware.providers.authn.cognito_provider import CognitoProvider

# Custom HTTP client with connection pooling
http_client = httpx.AsyncClient(
    timeout=30.0,
    limits=httpx.Limits(
        max_keepalive_connections=10,
        max_connections=100,
    )
)

# Note: This is conceptual - actual implementation may vary
cognito_provider = CognitoProvider(
    settings=auth_settings,
    http_client=http_client,
)

Caching Configuration

Token validation results can be cached to improve performance:

# This is conceptual - check actual provider documentation
auth_settings = CognitoAuthzProviderSettings(
    user_pool_id="us-east-1_abcdef123",
    user_pool_region="us-east-1",
    cache_jwks=True,              # Cache JSON Web Key Sets
    cache_ttl=3600,               # Cache TTL in seconds
)

Logging Configuration

Detailed Logging

Enable detailed logging for debugging:

import logging

# Configure auth middleware logging
auth_logger = logging.getLogger("auth_middleware")
auth_logger.setLevel(logging.DEBUG)

# Add handler
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
auth_logger.addHandler(handler)

Environment-based Logging

# Set log level via environment
AUTH_MIDDLEWARE_LOG_LEVEL=DEBUG
AUTH_MIDDLEWARE_LOG_FORMAT="%(asctime)s %(name)s %(levelname)s %(message)s"
AUTH_MIDDLEWARE_LOGGER_NAME="my_auth"

Security Best Practices

Token Security

  1. Use HTTPS: Always use HTTPS in production to protect tokens in transit

  2. Token Expiration: Ensure tokens have reasonable expiration times

  3. Secure Storage: Never log or store JWT tokens in plain text

  4. Rotation: Implement token rotation strategies

Configuration Security

  1. Environment Variables: Store sensitive configuration in environment variables

  2. Secrets Management: Use proper secrets management systems in production

  3. Least Privilege: Configure minimal required permissions

  4. Monitoring: Monitor authentication failures and suspicious activity

Error Handling Configuration

Custom Error Handlers

from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from auth_middleware.exceptions import (
    AuthenticationError,
    AuthorizationError,
    TokenExpiredError,
)

app = FastAPI()

@app.exception_handler(AuthenticationError)
async def authentication_error_handler(request, exc):
    return JSONResponse(
        status_code=401,
        content={
            "error": "Authentication Failed",
            "message": "Please provide a valid authentication token",
            "type": "authentication_error"
        }
    )

@app.exception_handler(AuthorizationError)
async def authorization_error_handler(request, exc):
    return JSONResponse(
        status_code=403,
        content={
            "error": "Access Denied",
            "message": "You don't have permission to access this resource",
            "type": "authorization_error"
        }
    )

@app.exception_handler(TokenExpiredError)
async def token_expired_handler(request, exc):
    return JSONResponse(
        status_code=401,
        content={
            "error": "Token Expired",
            "message": "Your authentication token has expired. Please login again",
            "type": "token_expired"
        }
    )

Development Configuration

Development Mode

For development and testing environments:

import os

# Development-friendly configuration
auth_settings = CognitoAuthzProviderSettings(
    user_pool_id=os.getenv("AWS_COGNITO_USER_POOL_ID"),
    user_pool_region=os.getenv("AWS_COGNITO_USER_POOL_REGION"),
    jwt_token_verification_disabled=os.getenv("ENVIRONMENT") == "development",
)

Hot Reloading

Auth Middleware works with FastAPI’s hot reloading in development:

# Development server with hot reloading
uvicorn main:app --reload --host 0.0.0.0 --port 8000

Testing Configuration

import pytest
from fastapi.testclient import TestClient

@pytest.fixture
def test_app():
    app = FastAPI()

    # Use mock auth provider for testing
    app.add_middleware(
        JwtAuthMiddleware,
        auth_provider=MockAuthProvider(),
    )

    return app

def test_protected_endpoint(test_app):
    client = TestClient(test_app)

    response = client.get(
        "/protected",
        headers={"Authorization": "Bearer test_token"}
    )

    assert response.status_code == 200

For more specific provider configurations, see the individual provider documentation: