Skip to main content

FastAPI middleware for Stytch B2B authentication with Redis caching

Project description

ayz-auth

FastAPI middleware for Stytch B2B authentication with Redis caching.

Overview

ayz-auth is a lightweight, production-ready authentication middleware for FastAPI applications using Stytch B2B authentication services. It provides session token verification with Redis caching for optimal performance and includes comprehensive error handling and logging.

Features

  • 🔐 Stytch B2B Integration: Seamless integration with Stytch B2B authentication
  • Redis Caching: Intelligent caching to reduce API calls and improve performance
  • 🚀 FastAPI Native: Built specifically for FastAPI with proper dependency injection
  • 📝 Type Safe: Full Pydantic models with type hints throughout
  • 🛡️ Security First: Secure token handling with configurable logging levels
  • 🔧 Configurable: Environment-based configuration with sensible defaults
  • 📊 Comprehensive Logging: Structured logging with sensitive data protection
  • 🧪 Well Tested: Comprehensive test suite with mocking support

Installation

pip install ayz-auth

Or with UV:

uv add ayz-auth

Quick Start

1. Environment Configuration

Create a .env file or set environment variables:

STYTCH_PROJECT_ID=your_project_id
STYTCH_SECRET=your_secret_key
STYTCH_ENVIRONMENT=test  # or "live" for production
REDIS_URL=redis://localhost:6379

2. Basic Usage

from fastapi import FastAPI, Depends
from ayz_auth import verify_auth, StytchContext

app = FastAPI()

@app.get("/protected")
async def protected_route(user: StytchContext = Depends(verify_auth)):
    return {
        "message": f"Hello {user.member_email}",
        "member_id": user.member_id,
        "organization_id": user.organization_id
    }

@app.get("/user-info")
async def get_user_info(user: StytchContext = Depends(verify_auth)):
    return {
        "member_id": user.member_id,
        "email": user.member_email,
        "name": user.member_name,
        "organization_id": user.organization_id,
        "session_expires_at": user.session_expires_at,
        "authentication_factors": user.authentication_factors
    }

3. Optional Authentication

For endpoints that work with or without authentication:

from typing import Optional
from ayz_auth import verify_auth_optional

@app.get("/optional-auth")
async def optional_route(user: Optional[StytchContext] = Depends(verify_auth_optional)):
    if user:
        return {"message": f"Hello {user.member_email}"}
    else:
        return {"message": "Hello anonymous user"}

4. Custom Authentication Requirements

Create custom dependencies with additional requirements:

from ayz_auth import create_auth_dependency

# Require specific custom claims
admin_auth = create_auth_dependency(required_claims=["admin"])
moderator_auth = create_auth_dependency(required_claims=["moderator", "verified"])

# Require specific authentication factors
mfa_auth = create_auth_dependency(required_factors=["mfa"])

@app.get("/admin")
async def admin_route(user: StytchContext = Depends(admin_auth)):
    return {"message": "Admin access granted"}

@app.get("/sensitive")
async def sensitive_route(user: StytchContext = Depends(mfa_auth)):
    return {"message": "MFA verified access"}

Configuration

All configuration is handled through environment variables with the STYTCH_ prefix:

Variable Default Description
STYTCH_PROJECT_ID required Your Stytch project ID
STYTCH_SECRET required Your Stytch secret key
STYTCH_ENVIRONMENT test Stytch environment (test or live)
STYTCH_REDIS_URL redis://localhost:6379 Redis connection URL
STYTCH_REDIS_PASSWORD None Redis password (if required)
STYTCH_REDIS_DB 0 Redis database number
STYTCH_REDIS_SSL False Use SSL for Redis connection
STYTCH_CACHE_TTL 300 Cache TTL in seconds (5 minutes)
STYTCH_CACHE_PREFIX ayz_auth Redis key prefix
STYTCH_LOG_LEVEL INFO Logging level
STYTCH_LOG_SENSITIVE_DATA False Log sensitive data (never in production)
STYTCH_REQUEST_TIMEOUT 10 Request timeout in seconds
STYTCH_MAX_RETRIES 3 Maximum retry attempts

StytchContext Model

The StytchContext model contains all the essential session data from Stytch:

class StytchContext(BaseModel):
    # Core identifiers
    member_id: str
    session_id: str
    organization_id: str
    
    # Session timing
    session_started_at: datetime
    session_expires_at: datetime
    session_last_accessed_at: datetime
    authenticated_at: datetime
    
    # Member information
    member_email: Optional[str]
    member_name: Optional[str]
    
    # Session metadata
    session_custom_claims: Dict[str, Any]
    authentication_factors: List[str]
    raw_session_data: Dict[str, Any]
    
    # Utility properties
    @property
    def is_expired(self) -> bool: ...
    
    @property
    def time_until_expiry(self) -> Optional[float]: ...

Error Handling

The middleware provides structured error responses:

# 401 Unauthorized - Missing or invalid token
{
    "error": "authentication_failed",
    "message": "Authorization header is required",
    "type": "token_extraction"
}

# 401 Unauthorized - Token verification failed
{
    "error": "authentication_failed", 
    "message": "Invalid or expired session token",
    "type": "token_verification"
}

# 503 Service Unavailable - Stytch API issues
{
    "error": "service_unavailable",
    "message": "Authentication service temporarily unavailable", 
    "type": "stytch_api"
}

# 403 Forbidden - Insufficient permissions (custom auth)
{
    "error": "insufficient_permissions",
    "message": "Missing required claims: ['admin']",
    "type": "authorization"
}

# 403 Forbidden - Insufficient authentication factors (custom auth)
{
    "error": "insufficient_authentication",
    "message": "Missing required authentication factors: ['mfa']",
    "type": "authorization"
}

Caching Strategy

The middleware implements a two-tier verification system:

  1. Redis Cache Check: Fast lookup of previously verified tokens
  2. Stytch API Fallback: Fresh verification when cache misses

Cache entries automatically expire based on the session expiration time, ensuring security while maximizing performance.

Integration with Your User System

Since the middleware only returns Stytch session data, you can easily integrate it with your existing user system:

from your_app.models import User
from your_app.database import get_user_by_stytch_member_id

@app.get("/profile")
async def get_profile(stytch: StytchContext = Depends(verify_auth)):
    # Use the member_id to fetch your user data
    user = await get_user_by_stytch_member_id(stytch.member_id)
    
    if not user:
        raise HTTPException(404, "User not found")
    
    # Check permissions using your user model
    if "read_profile" not in user.permissions:
        raise HTTPException(403, "Insufficient permissions")
    
    return {
        "stytch_data": stytch.to_dict(),
        "user_data": user.to_dict()
    }

Development

Running Tests

# Install development dependencies
uv sync --dev

# Run tests
pytest

# Run tests with coverage
pytest --cov=ayz_auth

Code Quality

# Format code
black src/ tests/
isort src/ tests/

# Lint code
ruff check src/ tests/

# Type checking
mypy src/

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass
  6. Submit a pull request

License

MIT License - see LICENSE file for details.

Support

For issues and questions:

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

ayz_auth-0.2.3.tar.gz (29.8 kB view details)

Uploaded Source

Built Distribution

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

ayz_auth-0.2.3-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file ayz_auth-0.2.3.tar.gz.

File metadata

  • Download URL: ayz_auth-0.2.3.tar.gz
  • Upload date:
  • Size: 29.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for ayz_auth-0.2.3.tar.gz
Algorithm Hash digest
SHA256 89a004de29994e0db77b8c8c44b1db2fd786d3abed56e4a470af261cc69460f6
MD5 5a929682bd25cc2605e93ce962b318c1
BLAKE2b-256 43c4bfde9fbb7c142fe7011de55501a55acb1aab1c7ada7d9b1ec956c7fe7fc2

See more details on using hashes here.

Provenance

The following attestation bundles were made for ayz_auth-0.2.3.tar.gz:

Publisher: ci-cd.yml on brandsoulmates/ayz-auth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ayz_auth-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: ayz_auth-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 19.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for ayz_auth-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 45d494dfec4ac1d38e67cbedae978890c966340fcd5c686fae77f4f4f2053936
MD5 55e2280ecc918d2020da6f606f01355e
BLAKE2b-256 eb0d3310ade027b4e9dac074fefb4afa401debb2e76a680bdd0c4d8d8e0d6225

See more details on using hashes here.

Provenance

The following attestation bundles were made for ayz_auth-0.2.3-py3-none-any.whl:

Publisher: ci-cd.yml on brandsoulmates/ayz-auth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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