Skip to main content

Enterprise CORS middleware presets for FastAPI applications

Project description

netrun-cors

Enterprise CORS middleware presets for FastAPI applications.

Overview

netrun-cors provides standardized, production-ready CORS configurations for FastAPI applications. Instead of manually configuring CORS middleware for each project, use battle-tested presets that follow OWASP security best practices.

Key Features:

  • 4 preset configurations: development, staging, production, oauth
  • OWASP compliance validation (prevents wildcard + credentials vulnerability)
  • OAuth platform support (Google, Twitter, LinkedIn, Facebook, TikTok, Instagram, Pinterest, Threads)
  • Azure Container Apps integration
  • Type-safe configuration with Pydantic
  • Comprehensive test coverage (90%+)

Installation

pip install netrun-cors

Or with Poetry:

poetry add netrun-cors

Quick Start

Development (Local Development)

Permissive CORS for rapid iteration on localhost:

from fastapi import FastAPI
from netrun_cors import CORSPreset

app = FastAPI()
app.add_middleware(CORSPreset.development())

Configuration:

  • Origins: localhost:3000, localhost:5173, 127.0.0.1:3000 (HTTP)
  • Credentials: Enabled
  • Methods: All (*)
  • Headers: All (*)
  • Max Age: 600s (10 minutes)

Production (Explicit Whitelist)

Strict origin whitelisting for production deployments:

from fastapi import FastAPI
from netrun_cors import CORSPreset

app = FastAPI()
app.add_middleware(
    CORSPreset.production(
        origins=[
            "https://app.example.com",
            "https://www.example.com"
        ]
    )
)

Configuration:

  • Origins: Explicit whitelist (HTTPS recommended)
  • Credentials: Enabled
  • Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
  • Headers: Authorization, Content-Type, Accept, X-Request-ID
  • Expose Headers: X-Request-ID
  • Max Age: 3600s (1 hour)

Staging (Azure Container Apps)

Support for Azure Container Apps with wildcard environment hashes:

from fastapi import FastAPI
from netrun_cors import CORSPreset

app = FastAPI()
app.add_middleware(
    CORSPreset.staging(
        container_apps=["intirkast-frontend", "meridian-frontend"],
        region="eastus2"
    )
)

Configuration:

  • Origins: https://localhost:3000, 5173 + Azure Container Apps wildcards
  • Credentials: Enabled
  • Methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
  • Headers: Authorization, Content-Type, Accept, X-Requested-With, X-Request-ID
  • Max Age: 1800s (30 minutes)

Optional Additional Origins:

app.add_middleware(
    CORSPreset.staging(
        container_apps=["test-app"],
        additional_origins=["https://staging.example.com"]
    )
)

OAuth (Social Platform Integration)

Whitelist OAuth callback domains for social login flows:

from fastapi import FastAPI
from netrun_cors import CORSPreset

app = FastAPI()
app.add_middleware(
    CORSPreset.oauth(
        app_origins=["https://app.intirkast.com"],
        platforms=["google", "twitter", "linkedin", "facebook"]
    )
)

Configuration:

  • Origins: App origins + OAuth platform domains
  • Credentials: Enabled
  • Methods: All (*)
  • Headers: All (*)
  • Expose Headers: All (*)
  • Max Age: 3600s (1 hour)

Supported Platforms:

Platform Domain
google https://accounts.google.com
twitter https://twitter.com
linkedin https://www.linkedin.com
facebook https://www.facebook.com
tiktok https://www.tiktok.com
instagram https://www.instagram.com
pinterest https://www.pinterest.com
threads https://www.threads.net

Advanced Usage

Custom Configuration

If presets don't match your requirements, use CORSPreset.custom():

from netrun_cors import CORSPreset

app.add_middleware(
    CORSPreset.custom(
        allow_origins=["https://app.example.com"],
        allow_credentials=True,
        allow_methods=["GET", "POST"],
        allow_headers=["Authorization", "Content-Type"],
        expose_headers=["X-Request-ID"],
        max_age=1200
    )
)

OWASP Compliance Validation:

The custom preset validates against OWASP security guidelines:

# ❌ This will raise ValueError
app.add_middleware(
    CORSPreset.custom(
        allow_origins=["*"],  # Wildcard
        allow_credentials=True  # Credentials enabled
    )
)
# ValueError: OWASP Violation: Cannot use wildcard origins with credentials

Environment Variable Configuration

Use CORSConfig for environment-based configuration:

from netrun_cors import CORSConfig

config = CORSConfig()
# Reads from environment variables:
# CORS_ORIGINS=https://app.example.com,https://www.example.com
# CORS_ALLOW_CREDENTIALS=true
# CORS_MAX_AGE=3600

app.add_middleware(
    CORSPreset.production(origins=config.CORS_ORIGINS)
)

.env file example:

CORS_ORIGINS=https://app.example.com,https://www.example.com
CORS_ALLOW_CREDENTIALS=true
CORS_ALLOW_METHODS=GET,POST,PUT,DELETE
CORS_ALLOW_HEADERS=Authorization,Content-Type
CORS_EXPOSE_HEADERS=X-Request-ID,X-Process-Time
CORS_MAX_AGE=3600

Migration Guide

Before: Manual CORS Configuration

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:3000",
        "http://localhost:5173",
        "https://app.intirkast.com",
        "https://intirkast-frontend.orangesmoke-f0fb748a.eastus2.azurecontainerapps.io",
        "https://accounts.google.com",  # Google OAuth
        "https://twitter.com",  # Twitter OAuth
        "https://www.linkedin.com",  # LinkedIn OAuth
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["X-Request-ID", "X-Process-Time"],
    max_age=3600,
)

LOC: 18 lines


After: netrun-cors

from fastapi import FastAPI
from netrun_cors import CORSPreset

app = FastAPI()
app.add_middleware(
    CORSPreset.oauth(
        app_origins=["https://app.intirkast.com"],
        platforms=["google", "twitter", "linkedin"]
    )
)

LOC: 2 lines (89% reduction)


Security Best Practices

1. Production Origin Whitelisting

Always use explicit origin lists in production:

# ✅ Secure (explicit origins)
app.add_middleware(
    CORSPreset.production(origins=["https://app.example.com"])
)

# ❌ Insecure (wildcard in production)
app.add_middleware(
    CORSPreset.custom(allow_origins=["*"], allow_credentials=False)
)

2. HTTPS Only in Production

Use HTTPS origins for production deployments:

# ✅ Secure (HTTPS)
origins = [
    "https://app.example.com",
    "https://www.example.com"
]

# ❌ Insecure (HTTP in production)
origins = [
    "http://app.example.com",  # Vulnerable to MITM attacks
]

3. Minimize Exposed Headers

Only expose headers your frontend needs:

# ✅ Minimal (production)
expose_headers = ["X-Request-ID"]

# ⚠️ Permissive (development only)
expose_headers = ["*"]

4. Preflight Caching

Use appropriate max_age for preflight caching:

# Production: 1 hour (reduces preflight overhead)
app.add_middleware(CORSPreset.production(origins=[...]))  # max_age=3600

# Development: 10 minutes (faster iteration)
app.add_middleware(CORSPreset.development())  # max_age=600

Testing

Run the test suite with pytest:

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=netrun_cors --cov-report=term-missing

# Run tests with verbose output
pytest -v

API Reference

CORSPreset

Factory class for standardized CORS middleware configurations.

Methods

development()

Permissive CORS for local development (localhost origins).

Returns: CORSMiddleware

Example:

app.add_middleware(CORSPreset.development())

staging(container_apps, region, additional_origins)

Azure Container Apps staging environment with wildcard support.

Parameters:

  • container_apps (List[str]): Azure Container App names
  • region (str): Azure region (default: "eastus2")
  • additional_origins (Optional[List[str]]): Additional origins to whitelist

Returns: CORSMiddleware

Example:

app.add_middleware(
    CORSPreset.staging(
        container_apps=["test-app"],
        region="westus2"
    )
)

production(origins)

Production deployment with explicit origin whitelisting.

Parameters:

  • origins (List[str]): List of allowed production origins

Returns: CORSMiddleware

Example:

app.add_middleware(
    CORSPreset.production(origins=["https://app.example.com"])
)

oauth(app_origins, platforms)

OAuth-enabled applications with social platform callback domains.

Parameters:

  • app_origins (List[str]): Application origins
  • platforms (List[str]): OAuth platform identifiers

Returns: CORSMiddleware

Raises:

  • ValueError: If invalid platform identifier provided

Example:

app.add_middleware(
    CORSPreset.oauth(
        app_origins=["https://app.example.com"],
        platforms=["google", "twitter"]
    )
)

custom(**kwargs)

Custom CORS configuration with full control.

Parameters:

  • allow_origins (List[str]): List of allowed origins
  • allow_credentials (bool): Allow credentials (default: True)
  • allow_methods (Optional[List[str]]): Allowed HTTP methods
  • allow_headers (Optional[List[str]]): Allowed headers
  • expose_headers (Optional[List[str]]): Exposed headers
  • max_age (int): Preflight cache duration in seconds

Returns: CORSMiddleware

Raises:

  • ValueError: If wildcard origins combined with credentials

Example:

app.add_middleware(
    CORSPreset.custom(
        allow_origins=["https://app.example.com"],
        allow_methods=["GET", "POST"],
        max_age=1200
    )
)

CORSConfig

Pydantic settings for type-safe CORS configuration with environment variable support.

Attributes:

  • CORS_ORIGINS (List[str]): Allowed origins (default: localhost)
  • CORS_ALLOW_CREDENTIALS (bool): Allow credentials (default: True)
  • CORS_ALLOW_METHODS (List[str]): Allowed methods (default: ["*"])
  • CORS_ALLOW_HEADERS (List[str]): Allowed headers (default: ["*"])
  • CORS_EXPOSE_HEADERS (List[str]): Exposed headers (default: ["X-Request-ID", "X-Process-Time"])
  • CORS_MAX_AGE (int): Preflight cache duration (default: 3600)

Example:

from netrun_cors import CORSConfig

config = CORSConfig()
print(config.CORS_ORIGINS)  # ['http://localhost:3000', 'http://localhost:5173']

Performance Considerations

Preflight Caching

CORS preflight requests (OPTIONS) can add latency. Use max_age to cache preflight responses:

Environment max_age Rationale
Development 600s (10 min) Fast iteration, frequent changes
Staging 1800s (30 min) Balance between testing and performance
Production 3600s (1 hour) Maximum performance, stable configuration

Origin Matching Performance

Explicit origin lists have O(n) lookup time. For large origin lists (100+), consider:

  1. Using a reverse proxy (Nginx, Cloudflare) for CORS
  2. Implementing origin caching in your application
  3. Grouping origins by environment (dev, staging, prod)

Troubleshooting

Common Issues

1. CORS Error: "No 'Access-Control-Allow-Origin' header"

Cause: Origin not whitelisted in CORS configuration

Solution: Add origin to allowed list:

app.add_middleware(
    CORSPreset.production(
        origins=[
            "https://app.example.com",
            "https://new-origin.example.com"  # Add missing origin
        ]
    )
)

2. CORS Error: "Wildcard with credentials"

Cause: Attempting to use allow_origins=["*"] with allow_credentials=True

Solution: Use explicit origins or disable credentials:

# Option 1: Explicit origins (recommended)
app.add_middleware(
    CORSPreset.production(origins=["https://app.example.com"])
)

# Option 2: Disable credentials
app.add_middleware(
    CORSPreset.custom(allow_origins=["*"], allow_credentials=False)
)

3. OAuth Callback Failure

Cause: OAuth platform domain not whitelisted

Solution: Use oauth preset with appropriate platforms:

app.add_middleware(
    CORSPreset.oauth(
        app_origins=["https://app.example.com"],
        platforms=["google", "twitter", "linkedin"]
    )
)

4. Azure Container Apps Staging Issues

Cause: Dynamic environment hash not matching

Solution: Use staging preset with wildcard support:

app.add_middleware(
    CORSPreset.staging(
        container_apps=["intirkast-frontend"],
        region="eastus2"
    )
)

Contributing

Contributions welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/new-preset)
  3. Write tests for new functionality
  4. Ensure tests pass (pytest)
  5. Run linters (black, ruff, mypy)
  6. Submit a pull request

License

MIT License - see LICENSE file for details.


Support

Issues: GitHub Issues Email: support@netrunsystems.com Documentation: GitHub Repository


Changelog

v1.0.0 (2025-11-25)

Initial Release:

  • 4 preset configurations (development, staging, production, oauth)
  • OWASP compliance validation
  • 8 OAuth platform support
  • Azure Container Apps integration
  • Pydantic configuration support
  • Comprehensive test suite (90%+ coverage)

Developed by Netrun Systems

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

netrun_cors-1.0.0.tar.gz (14.5 kB view details)

Uploaded Source

Built Distribution

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

netrun_cors-1.0.0-py3-none-any.whl (11.5 kB view details)

Uploaded Python 3

File details

Details for the file netrun_cors-1.0.0.tar.gz.

File metadata

  • Download URL: netrun_cors-1.0.0.tar.gz
  • Upload date:
  • Size: 14.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for netrun_cors-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c52e33a8a5f9a0ffb89cdd8f023b66c1b451fe5d01f8dc3fd9f987cf4b507139
MD5 bc0fd6361f22f2a3d7f66ed6fd8013bf
BLAKE2b-256 1b8c338b9e45a380ac1588b83824f24be61e915545128f4fe7f9c5f39a18da9c

See more details on using hashes here.

File details

Details for the file netrun_cors-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: netrun_cors-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 11.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for netrun_cors-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a37551ad2df5156fa0c7fd6a75a56674c4923237c8340c63da09beeb1fc5006b
MD5 b125b13c6a2095b686d535642d3087ab
BLAKE2b-256 ba01a962fd0bdcf8f5bb024d1204e002a7672fbe751dfaff2b49f1854f6a6903

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