How Open Notebook's PasswordAuthMiddleware Implements Password Authentication
Open Notebook's PasswordAuthMiddleware validates Bearer tokens against an environment‑configured password, protecting all API routes except explicitly excluded paths like /health and /docs.
The lfnovo/open-notebook repository uses a custom FastAPI middleware to enforce password authentication across its REST API. The PasswordAuthMiddleware intercepts incoming requests, verifies Bearer tokens against a secret stored in environment variables or Docker secrets, and rejects unauthorized access with standard HTTP 401 responses. This implementation ensures that sensitive notebook operations remain protected while allowing public access to health checks and documentation endpoints.
Architecture of PasswordAuthMiddleware
The middleware is implemented as a standard FastAPI ASGI middleware class defined in api/auth.py (lines 12‑78). It operates by inspecting every incoming request before it reaches route handlers, performing early exit checks for excluded paths and CORS preflight requests, then validating the Authorization header against the configured password.
Core Implementation in api/auth.py
Initialization and Configuration
The middleware constructor loads the password using get_secret_from_env("OPEN_NOTEBOOK_PASSWORD"), which reads from environment variables or Docker secret files. It also accepts an excluded_paths parameter defaulting to common public routes:
self.password = get_secret_from_env("OPEN_NOTEBOOK_PASSWORD")
self.excluded_paths = excluded_paths or ["/", "/health", "/docs", "/openapi.json", "/redoc"]
Request Filtering Logic
The middleware applies three early exit conditions before validating credentials:
- No password configured: If
self.passwordis empty, all requests pass through immediately, enabling local development without authentication. - Excluded paths: Routes listed in
self.excluded_pathsbypass authentication entirely. - CORS preflight:
OPTIONSrequests are forwarded untouched to prevent browser cross-origin blocking.
if not self.password:
return await call_next(request)
if request.url.path in self.excluded_paths:
return await call_next(request)
if request.method == "OPTIONS":
return await call_next(request)
Bearer Token Validation
For protected routes, the middleware requires an Authorization: Bearer <password> header. Missing headers trigger a 401 response with WWW-Authenticate: Bearer challenge:
auth_header = request.headers.get("Authorization")
if not auth_header:
return JSONResponse(
status_code=401,
content={"detail": "Missing authorization header"},
headers={"WWW-Authenticate": "Bearer"}
)
After extracting the credential, the middleware compares it against the stored password. Mismatches return identical 401 responses:
if credentials != self.password:
return JSONResponse(
status_code=401,
content={"detail": "Invalid password"},
headers={"WWW-Authenticate": "Bearer"}
)
Upon successful validation, the request proceeds via await call_next(request).
Integration with the FastAPI Application
In api/main.py (lines 73‑86), the middleware is registered before CORS handling to ensure authentication runs first:
from api.auth import PasswordAuthMiddleware
app.add_middleware(
PasswordAuthMiddleware,
excluded_paths=[
"/", "/health", "/docs", "/openapi.json", "/redoc",
"/api/auth/status", "/api/config",
],
)
Notable exclusions include /api/auth/status (used by the frontend to detect password requirements) and /api/config (public configuration endpoint).
Optional Route-Level Protection
While the middleware provides global protection, individual routes can use the check_api_password dependency for explicit enforcement:
from fastapi import APIRouter, Depends
from api.auth import check_api_password
router = APIRouter()
@router.get("/secret")
async def secret_endpoint(valid: bool = Depends(check_api_password)):
return {"msg": "You are authenticated!"}
Configuration via Environment Variables
The password source is configured through OPEN_NOTEBOOK_PASSWORD or the file-based OPEN_NOTEBOOK_PASSWORD_FILE used by Docker secrets:
# Development .env
OPEN_NOTEBOOK_PASSWORD=my-secret-pass
# Production Docker secret
# Set OPEN_NOTEBOOK_PASSWORD_FILE=/run/secrets/notebook_pass
Summary
- Global protection:
PasswordAuthMiddlewareinapi/auth.pyintercepts all requests and enforces Bearer token authentication. - Flexible configuration: Passwords load from environment variables or Docker secrets via
get_secret_from_env(). - Selective exclusions: Routes like
/health,/docs, and/api/auth/statusbypass authentication through configurable path lists. - Standards-compliant: Invalid or missing credentials produce RFC 6750-compliant 401 responses with
WWW-Authenticate: Bearerheaders. - Optional granularity: The
check_api_passworddependency allows per-route authentication reinforcement when needed.
Frequently Asked Questions
Where is PasswordAuthMiddleware defined in the Open Notebook repository?
The middleware class is defined in api/auth.py (lines 12‑78). This file also contains the check_api_password dependency function for route-level authentication.
How does the middleware handle development environments without passwords?
When OPEN_NOTEBOOK_PASSWORD is unset, the initialization logic sets self.password to an empty value. The dispatch method checks this condition first and immediately forwards requests via await call_next(request), effectively disabling authentication for local development.
Why are OPTIONS requests excluded from password validation?
The middleware explicitly skips OPTIONS requests to support CORS preflight checks performed by browsers before cross-origin requests. Blocking these would prevent web clients from determining allowed HTTP methods and headers.
Can I add additional public routes that bypass authentication?
Yes. When registering the middleware in api/main.py, extend the excluded_paths list to include your custom public endpoints. The default configuration already excludes /api/auth/status and /api/config in addition to standard documentation paths.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →