# How PasswordAuthMiddleware Protects API Endpoints with Bearer Tokens in Open Notebook

> Learn how PasswordAuthMiddleware secures your API endpoints with bearer token authentication in Open Notebook. Discover how it blocks unauthorized access while allowing essential requests.

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

---

**`PasswordAuthMiddleware` is a FastAPI/Starlette middleware that intercepts every HTTP request to enforce Bearer token authentication using a single password, while allowing specific paths and CORS pre-flight requests to remain accessible.**

The `lfnovo/open-notebook` project uses `PasswordAuthMiddleware` to secure its REST API with a simple, environment-based authentication mechanism. This middleware acts as a global gatekeeper, validating `Authorization` headers against a configured secret before allowing requests to reach route handlers.

## Environment-Based Password Configuration

At startup, the middleware initializes by loading the secret password via `get_secret_from_env` from [`open_notebook/utils/encryption.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/encryption.py). It checks for the `OPEN_NOTEBOOK_PASSWORD` environment variable or a corresponding Docker secret file. 

If no password is configured, the middleware enters a no-op mode, allowing the API to run without authentication—ideal for local development scenarios.

## Path-Based Access Control

The middleware accepts an `excluded_paths` parameter during registration in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) to whitelist specific endpoints. By default, the root path (`/`), health checks (`/health`), documentation (`/docs`, `/redoc`, [`/openapi.json`](https://github.com/lfnovo/open-notebook/blob/main//openapi.json)), authentication status (`/api/auth/status`), and configuration endpoints (`/api/config`) bypass password verification.

This ensures public accessibility for status checks and API documentation while protecting business logic routes.

## Bearer Token Validation Logic

For non-excluded paths, the middleware enforces strict header validation according to the following workflow:

1. **Missing Header**: Returns **401 Unauthorized** with a `WWW-Authenticate: Bearer` challenge header.
2. **Invalid Format**: Validates the `Authorization` header follows the `Bearer {password}` format; deviations return **401** with "Invalid authorization header format".
3. **Password Mismatch**: Compares the extracted token against the stored secret; mismatches return **401 Invalid password**.
4. **Success**: Calls `call_next` to forward the request to the route handler and returns the response unchanged.

## CORS Pre-flight Bypass

OPTIONS requests used for CORS pre-flight checks are automatically allowed without authentication validation. This prevents cross-origin request failures during browser-based API interactions while maintaining security for actual data-modifying operations.

## Implementing the Middleware in FastAPI

Register the middleware in your FastAPI application entry point to enable global protection:

```python

# api/main.py – middleware registration

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

```

This configuration applies the authentication layer to every incoming request while preserving access to essential metadata and health monitoring endpoints.

## Route-Level Authentication with check_api_password

For scenarios requiring explicit authentication on specific routes rather than 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 function. This allows individual endpoints to enforce the same Bearer token validation without affecting the entire application surface.

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

router = APIRouter()

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

```

Use this approach when mixing public and protected routes within the same router or when you need to bypass the middleware for specific request types.

## Client Authentication Examples

Access protected endpoints by including the Bearer token in the Authorization header:

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

```

Failed authentication attempts return clear error messages:

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

```

Missing headers trigger the authentication challenge:

```json
{
  "detail": "Not authenticated"
}

```

## Summary

- **Global protection**: `PasswordAuthMiddleware` in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) intercepts all HTTP requests before they reach FastAPI routers.
- **Flexible configuration**: Supports environment variable (`OPEN_NOTEBOOK_PASSWORD`) or Docker secret file loading via `get_secret_from_env`.
- **Selective exemptions**: Configurable `excluded_paths` allow public access to health checks, documentation, and status endpoints.
- **Standard Bearer scheme**: Validates `Authorization: Bearer {password}` headers with proper 401 responses and `WWW-Authenticate` challenges.
- **Dual implementation**: Offers both middleware-level (global) and dependency-level (route-specific) protection via `check_api_password`.

## Frequently Asked Questions

### How does PasswordAuthMiddleware handle missing authentication headers?

When the `Authorization` header is absent, the middleware returns an HTTP 401 Unauthorized response with a `WWW-Authenticate: Bearer` header, prompting clients to provide credentials according to RFC 6750.

### Can I disable authentication entirely for local development?

Yes. If `OPEN_NOTEBOOK_PASSWORD` is not set in the environment and no Docker secret file is found, `PasswordAuthMiddleware` automatically becomes a pass-through (no-op), allowing all requests without validation.

### What endpoints are excluded from password protection by default?

The default configuration in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) excludes the root path (`/`), `/health`, `/docs`, `/redoc`, [`/openapi.json`](https://github.com/lfnovo/open-notebook/blob/main//openapi.json), `/api/auth/status`, and `/api/config` from authentication requirements.

### How do I protect a single route without enabling global middleware?

Import `check_api_password` from [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) and use it as a FastAPI dependency (`Depends(check_api_password)`) on specific route handlers. This enforces Bearer token validation only for that endpoint while leaving others unprotected.