# How PasswordAuthMiddleware Protects API Endpoints in Open Notebook

> Learn how PasswordAuthMiddleware in Open Notebook secures your API endpoints by enforcing Bearer token authentication and allowing specific public endpoints to bypass verification.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-18

---

**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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py). Public endpoints such as `/health`, `/docs`, [`/openapi.json`](https://github.com/lfnovo/open-notebook/blob/main//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`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), the application registers `PasswordAuthMiddleware` with specific exclusions:

```python

# 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:

```python
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:

```bash
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:

```json
{
  "detail": "Invalid password"
}

```

## Summary

- `PasswordAuthMiddleware` intercepts all HTTP requests in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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.