# How PasswordAuthMiddleware Secures API Endpoints in Open Notebook

> Discover how PasswordAuthMiddleware secures API endpoints in Open Notebook. This FastAPI middleware validates Bearer tokens against environment variables, ensuring robust API security.

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

---

**PasswordAuthMiddleware is a FastAPI middleware that intercepts every incoming request to verify a Bearer token against the `OPEN_NOTEBOOK_PASSWORD` environment variable, returning 401 Unauthorized for missing or invalid credentials while allowing configured public paths to bypass authentication.**

The `open-notebook` project implements a lightweight authentication layer using Starlette-based middleware to protect its REST API surface. Located in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py), the `PasswordAuthMiddleware` class provides a single-password gate that validates HTTP requests before they reach your route handlers, making it ideal for self-hosted deployments where simplicity outweighs complex user management.

## How PasswordAuthMiddleware Works

The middleware implements a seven-step protection strategy that processes every request before it reaches the application routers.

### Password Source and Initialization

At startup, the middleware initializes by reading the secret `OPEN_NOTEBOOK_PASSWORD` environment variable (or Docker secret file) via the `get_secret_from_env` helper in [`open_notebook/utils/encryption.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/encryption.py). If this variable is missing, the middleware becomes a no-op, allowing the API to run without authentication. This design supports local development environments where password protection is unnecessary.

### Path-Based Exclusions

The middleware accepts an `excluded_paths` parameter during registration in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py). Requests matching any path in this list bypass the password check entirely. Common exclusions include root paths, health checks, and API documentation endpoints.

```python

# api/main.py – middleware registration with excluded paths

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

```

### CORS Pre-flight Handling

OPTIONS requests for CORS pre-flight checks are automatically allowed without authentication. This prevents browsers from blocking legitimate cross-origin requests before the actual API call occurs.

### Header Validation and Authentication Flow

For all non-excluded requests, the middleware enforces a strict validation sequence:

1. **Presence Check**: If the `Authorization` header is missing, the middleware returns **401 Unauthorized** with a `WWW-Authenticate: Bearer` header to prompt credential submission.

2. **Format Validation**: The header must conform to the pattern `Bearer {password}`. Malformed headers return **401** with the message "Invalid authorization header format".

3. **Password Verification**: The extracted token is compared to the stored password. A mismatch yields **401** with "Invalid password".

4. **Request Forwarding**: When validation succeeds, the middleware hands the request to the downstream handler via `call_next` and returns the response unchanged.

## Route-Level Protection with check_api_password

Beyond global middleware, the [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) module exports `check_api_password`, a FastAPI dependency that individual routes can use to enforce authentication without applying the middleware to the entire application. This is useful for securing specific admin endpoints while keeping the rest of the API public.

```python
from api.auth import check_api_password

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

```

## Client Authentication Examples

When calling protected endpoints, clients must include the password in the Authorization header using the Bearer scheme.

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

```

If authentication fails, the server responds with a 401 status code and a JSON detail message:

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

```

Missing headers trigger a 401 response with the `WWW-Authenticate: Bearer` challenge header, prompting clients to resubmit with credentials.

## Summary

- **PasswordAuthMiddleware** intercepts all HTTP requests in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) before they reach FastAPI routers.
- It reads the `OPEN_NOTEBOOK_PASSWORD` from environment variables via `get_secret_from_env`, becoming a no-op if unset.
- The **excluded_paths** parameter in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) allows public access to health checks, documentation, and configuration endpoints.
- **OPTIONS requests** bypass authentication to support CORS pre-flight checks.
- Invalid or missing Bearer tokens return **401 Unauthorized** with specific error messages.
- The **check_api_password** dependency enables per-route authentication without global middleware.

## Frequently Asked Questions

### What happens if OPEN_NOTEBOOK_PASSWORD is not set?

When the `OPEN_NOTEBOOK_PASSWORD` environment variable is missing, the middleware initializes in a no-op state. All requests pass through without authentication, allowing the API to run in unsecured mode—typically used for local development environments.

### How do I exclude specific endpoints from authentication?

Pass a list of paths to the `excluded_paths` parameter when adding the middleware in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py). Any request whose path matches an entry in this list bypasses the password check entirely. Common exclusions include `/health`, `/docs`, [`/openapi.json`](https://github.com/lfnovo/open-notebook/blob/main//openapi.json), and `/redoc`.

### Can I use PasswordAuthMiddleware for individual routes only?

Yes. Instead of applying the middleware globally, import the `check_api_password` dependency from [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) and add it to specific route handlers using FastAPI's `Depends` mechanism. This approach secures only selected endpoints while leaving others publicly accessible.

### What is the correct format for the Authorization header?

The middleware expects the header in the format `Authorization: Bearer {password}` where `{password}` matches the value stored in `OPEN_NOTEBOOK_PASSWORD`. Requests missing the "Bearer" prefix or containing malformed tokens receive a 401 response with "Invalid authorization header format".