Configuring CORS for Open Notebook: Best Practices and Security Implementation

Open Notebook configures CORS through FastAPI's CORSMiddleware using the CORS_ORIGINS environment variable, defaulting to a wildcard (*) in development while integrating with password authentication middleware to enforce strict origin validation in production.

Configuring CORS for Open Notebook is critical for securing the FastAPI backend against cross-origin attacks while enabling legitimate browser-based frontend interactions. The application implements a sophisticated middleware stack in api/main.py that handles origin parsing, credential passthrough, and custom error response headers. Understanding these implementation details ensures safe deployments across development, single-tenant, and multi-tenant environments.

How CORS Configuration Works

Parsing Environment Variables

At startup, Open Notebook reads the CORS_ORIGINS environment variable in api/main.py (lines 53-58) using the helper function _parse_cors_origins. This function transforms comma-separated URLs into a Python list that feeds directly into the middleware configuration, allowing multiple origins for multi-tenant scenarios.

Middleware Registration

The CORSMiddleware is registered in api/main.py (lines 88-95) with allow_credentials=True, allow_methods=["*"], and allow_headers=["*"]. Critically, this middleware is added after the PasswordAuthMiddleware in the code; due to FastAPI's execution order, this means CORS processing runs first on every request, ensuring browsers receive proper headers even when subsequent authentication fails.

Default Wildcard Behavior and Warnings

If CORS_ORIGINS is unset, the system defaults to a permissive * wildcard that allows any origin. The application detects this condition in api/main.py (lines 63-71) and emits a startup warning to remind operators to restrict origins before deploying to production. This default facilitates local development but exposes the API to cross-origin attacks if left unconfigured.

Security Architecture and Middleware Ordering

Open Notebook implements defense-in-depth by layering CORS controls with the PasswordAuthMiddleware (defined in api/auth.py and instantiated in api/main.py lines 15-18). This middleware secures all routes except whitelisted endpoints like /api/auth/status and /api/config.

The middleware stack ordering is architecturally significant: because CORS middleware runs before authentication, rejected credentials still return proper Access-Control-Allow-Origin headers to the browser. This prevents CORS errors from masking authentication failures, allowing frontend developers to see actual 401/403 responses in developer tools.

Error Handling and CORS Headers

The application includes a custom _cors_headers helper function in api/main.py (lines 66-84) that injects CORS headers into all exception responses. Exception handlers starting at line 98 utilize this helper to ensure that 4xx client errors and 5xx server errors include appropriate Access-Control-Allow-Origin headers. This guarantees legitimate browsers can read error payloads from failed requests while maintaining origin restrictions for disallowed domains.

Local Development: Set CORS_ORIGINS=http://localhost:3000 to restrict access to your local frontend, minimizing the attack surface even during development.

Production Single-Tenant: Configure CORS_ORIGINS=https://notebook.example.com with your exact production domain. This prevents cross-site request forgery by ensuring only your deployed UI can request resources.

Multi-Tenant or SaaS: Explicitly enumerate tenant domains: CORS_ORIGINS=https://tenant1.example.com,https://tenant2.example.com. Never use wildcard patterns or dynamic origin matching when credentials are enabled.

Public APIs Without Authentication: Only use CORS_ORIGINS=* for truly stateless, public endpoints that require no credentials. Never combine wildcards with the Authorization header or cookie-based authentication, as browsers explicitly reject credential requests when servers return Access-Control-Allow-Origin: *.

Critical Security Considerations

Credentials Require Specific Origins: When allow_credentials=True (as implemented in the middleware), browsers require the specific requesting origin in the Access-Control-Allow-Origin response header. The _cors_headers function reflects the request origin only if it appears in the allowed list, satisfying browser security requirements while blocking unauthorized origins.

Encryption Key Validation: The application validates OPEN_NOTEBOOK_ENCRYPTION_KEY during the lifespan check in api/main.py (lines 104-114). This ensures credential storage encryption is active before accepting authenticated cross-origin requests, preventing plaintext credential exposure.

CI/CD Enforcement: Treat the wildcard startup warning as a hard failure in deployment pipelines. Configure infrastructure-as-code checks to verify that CORS_ORIGINS is explicitly set and does not contain * in production environments.

Implementation and Verification

Configure your production environment:


# .env

CORS_ORIGINS=https://notebook.mycompany.com
OPEN_NOTEBOOK_ENCRYPTION_KEY=minimum-32-character-secret-key-for-credential-encryption

You can verify the configuration using curl to inspect response headers:

curl -i -H "Origin: https://notebook.mycompany.com" \
     -H "Authorization: Bearer <your-token>" \
     http://localhost:5055/api/config

Expected secure response headers:


Access-Control-Allow-Origin: https://notebook.mycompany.com
Access-Control-Allow-Credentials: true

Key Source Files

  • api/main.py: FastAPI application factory containing _parse_cors_origins, CORSMiddleware registration (lines 88-95), _cors_headers helper (lines 66-84), and lifespan encryption validation (lines 104-114).
  • api/auth.py: Password authentication middleware that executes after CORS processing on the request path.
  • api/routers/config.py: Exposes configuration endpoints and emits CORS wildcard warnings.
  • open_notebook/utils/encryption.py: Manages the OPEN_NOTEBOOK_ENCRYPTION_KEY for secure credential storage.
  • open_notebook/config.py: Centralized environment variable handling.

Summary

  • Configure CORS_ORIGINS explicitly to prevent the default wildcard from exposing administrative endpoints to cross-origin attacks.
  • The middleware stack places CORS before authentication, ensuring browsers receive headers on rejected requests and can display proper error messages.
  • When allow_credentials=True, never use wildcard origins—browsers reject credential requests when Access-Control-Allow-Origin: * is returned.
  • Use the _cors_headers helper in exception handlers to ensure error responses remain readable by legitimate frontends while maintaining security boundaries.

Frequently Asked Questions

What happens if I leave CORS_ORIGINS unset in production?

The application defaults to * (allowing all origins) and logs a startup warning. While functional for local development, this configuration exposes authenticated endpoints to cross-origin attacks and prevents credential-based authentication from working in browsers due to security restrictions on wildcards with credentials.

Why does the CORS middleware run before authentication checks?

In api/main.py, the CORSMiddleware is added after PasswordAuthMiddleware in code registration (lines 88-95), but FastAPI executes middleware in reverse order of addition. This architectural choice ensures that CORS headers are present in responses to rejected authentication requests, preventing browsers from masking 401/403 errors behind CORS policy failures.

Can I use wildcard origins with API authentication enabled?

No. Open Notebook sets allow_credentials=True in the CORS middleware, which triggers browser security policies requiring specific origins. When the server returns Access-Control-Allow-Origin: *, browsers automatically reject requests containing cookies or Authorization headers, breaking authentication completely.

How do I verify my CORS configuration is secure?

Send a curl request with a spoofed origin header: curl -i -H "Origin: https://evil.com" http://localhost:5055/api/config. Verify that the response does not contain Access-Control-Allow-Origin: https://evil.com or wildcards. Then test with your legitimate domain to confirm Access-Control-Allow-Credentials: true appears only for allowed origins.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →