How PasswordAuthMiddleware Protects API Endpoints in Open Notebook

PasswordAuthMiddleware is a FastAPI/Starlette middleware that intercepts every incoming HTTP request to enforce Bearer token authentication using a single password, while allowing specific public endpoints and CORS pre-flight requests to bypass verification.

The lfnovo/open-notebook project uses PasswordAuthMiddleware to secure its FastAPI application with a simple, single-password gate. Implemented in api/auth.py, this middleware runs on every request to validate credentials before reaching any route handler, making it ideal for protecting notebook data in self-hosted deployments.

Middleware Architecture and Authentication Flow

PasswordAuthMiddleware follows a sequential validation pipeline that checks requests against multiple criteria before allowing access to protected resources.

Password Source and Initialization

At startup, the middleware loads the secret from the OPEN_NOTEBOOK_PASSWORD environment variable using get_secret_from_env from open_notebook/utils/encryption.py. This helper also supports Docker secrets by reading from file paths specified in environment variables. If no password is configured, the middleware becomes a no-op pass-through, enabling local development without authentication barriers.

Request Path Filtering and CORS Handling

The middleware first checks if the request path appears in the excluded_paths list supplied during registration in api/main.py. Public endpoints such as /health, /docs, /openapi.json, /redoc, /api/auth/status, and /api/config bypass authentication entirely. Additionally, all HTTP OPTIONS requests are allowed to proceed without validation to support CORS pre-flight checks.

Bearer Token Validation Logic

For protected routes, the middleware inspects the Authorization header with strict validation rules:

  • Missing header: Returns 401 Unauthorized with a WWW-Authenticate: Bearer response header
  • Invalid format: If the header lacks the Bearer {password} structure, returns 401 with "Invalid authorization header format"
  • Password mismatch: Compares the extracted token against the stored secret; mismatch yields 401 with "Invalid password"
  • Success: Calls call_next to forward the request to downstream handlers

Configuring Global Middleware

Register the middleware in your FastAPI application constructor to protect all routes by default. In api/main.py, the application registers PasswordAuthMiddleware with specific exclusions:


# api/main.py

from api.auth import PasswordAuthMiddleware

app.add_middleware(
    PasswordAuthMiddleware,
    excluded_paths=[
        "/", "/health", "/docs", "/openapi.json", "/redoc",
        "/api/auth/status", "/api/config",
    ],
)

This configuration ensures that documentation endpoints and system health checks remain accessible without credentials while the rest of the API requires authentication.

Route-Level Authentication

For endpoints that need individual protection without global middleware, the module exports check_api_password, a FastAPI dependency that enforces the same Bearer token validation. Use this in specific routers to secure admin functions or sensitive data operations:

from fastapi import Depends, APIRouter
from api.auth import check_api_password

router = APIRouter()

@router.get("/admin")
async def admin_panel(valid: bool = Depends(check_api_password)):
    return {"msg": "You are authorized"}

This dependency performs the same header extraction and password comparison logic defined in the middleware, ensuring consistent security across the application.

Client Integration Examples

Authenticated clients must include the password in the Authorization header using the Bearer scheme. Using curl to access a protected notebook endpoint:

curl -H "Authorization: Bearer my-secret-pw" \
     http://localhost:5055/api/notebooks

If the password is incorrect or missing, the server responds with HTTP 401 and a JSON detail message:

{
  "detail": "Invalid password"
}

Summary

  • PasswordAuthMiddleware intercepts all HTTP requests in api/auth.py before they reach route handlers
  • Authentication relies on the OPEN_NOTEBOOK_PASSWORD environment variable or Docker secrets loaded via get_secret_from_env
  • Excluded paths defined in api/main.py allow public access to documentation and health endpoints
  • CORS pre-flight OPTIONS requests bypass validation automatically
  • The middleware returns 401 Unauthorized for missing, malformed, or incorrect Bearer tokens
  • Individual routes can use the check_api_password dependency for selective protection without global middleware

Frequently Asked Questions

What happens if OPEN_NOTEBOOK_PASSWORD is not set?

If the environment variable is missing, PasswordAuthMiddleware initializes in a no-op mode and allows all requests to pass through unauthenticated. This behavior supports local development but should not be used in production deployments.

Can I use PasswordAuthMiddleware with multiple passwords or user accounts?

No, the middleware implements single-password authentication only. It performs a direct string comparison between the Bearer token and the stored secret, making it suitable for simple deployments but not multi-user scenarios requiring distinct credentials.

How do I protect a specific route while keeping the middleware disabled globally?

Import check_api_password from api/auth.py and add it as a dependency to individual endpoint functions using FastAPI's Depends. This dependency performs the same validation as the middleware without affecting other routes.

Does the middleware support HTTPS or encrypted passwords?

The middleware itself handles plaintext Bearer tokens. For production security, deploy the application behind an HTTPS reverse proxy to encrypt headers in transit. The password comparison occurs against the plaintext value loaded from OPEN_NOTEBOOK_PASSWORD or Docker secrets.

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 →