How to Set Up Password Authentication Middleware for Production in Open Notebook
To secure Open Notebook in production, set the OPEN_NOTEBOOK_PASSWORD environment variable (or mount a Docker secret to OPEN_NOTEBOOK_PASSWORD_FILE) so that the PasswordAuthMiddleware requires an Authorization: Bearer <password> header on every request.
Open Notebook (the lfnovo/open-notebook repository) provides a FastAPI-based HTTP API that runs without authentication in development for convenience. For production environments, you must enable the built-in password authentication middleware to protect sensitive notebook data. This configuration relies on the PasswordAuthMiddleware class defined in api/auth.py and registered in api/main.py (lines 73‑86), which validates Bearer tokens against a secret you provide via environment variables or Docker secrets.
How the Password Authentication Middleware Works
The middleware is instantiated in api/main.py and wraps the entire FastAPI application. When OPEN_NOTEBOOK_PASSWORD is present, every incoming request—except for a configured list of public paths—must include the header Authorization: Bearer <password> where <password> matches the configured secret.
The middleware is deliberately added before the CORS middleware (see app.add_middleware(PasswordAuthMiddleware, …) in api/main.py). This ensures that unauthenticated requests are rejected before any cross-origin handling occurs, preventing unauthorized access from reaching your business logic. Excluded paths typically include health-check endpoints, OpenAPI documentation (/docs, /redoc), and internal status routes, allowing monitoring tools to reach the service without credentials.
Configuring Production Secrets
The actual password value is retrieved via get_secret_from_env in open_notebook/utils/encryption.py (lines 29‑59). This helper supports two secure input methods:
Using Environment Variables
Set the secret directly in your shell or .env file:
export OPEN_NOTEBOOK_PASSWORD=SuperSecretProdPass
Restart the API process after setting this variable; the middleware reads the value once at startup.
Using Docker Secrets
For Docker Swarm or Kubernetes deployments, store the password in a file and reference it via the _FILE suffix variable:
# Create the secret file
echo "SuperSecretProdPass" > ./secrets/open_notebook_password.txt
Then mount it as a secret and set OPEN_NOTEBOOK_PASSWORD_FILE=/run/secrets/open_notebook_password (or let Docker Compose handle the path automatically).
Docker-Compose Configuration Example
Below is a production-ready docker-compose.yml that mounts the password as a Docker secret rather than exposing it in environment variables:
services:
api:
image: lfnovo/open-notebook:latest
env_file: .env.production # contains CORS_ORIGINS, etc.
secrets:
- open_notebook_password
ports:
- "5055:5055"
secrets:
open_notebook_password:
file: ./secrets/open_notebook_password.txt
If you prefer environment variables over secret files, omit the secrets: section and add the variable directly to env_file: .env.production:
OPEN_NOTEBOOK_PASSWORD=SuperSecretProdPass
CORS_ORIGINS=https://notebook.example.com
Client-Side Authentication
Once the middleware is active, all API clients must include the authorization header.
Python Client Automatic Handling
The built-in client in api/client.py automatically detects the OPEN_NOTEBOOK_PASSWORD environment variable and injects the header for you:
import os
from api.client import OpenNotebookClient
# Ensure the password is visible to the process
os.environ["OPEN_NOTEBOOK_PASSWORD"] = "SuperSecretProdPass"
client = OpenNotebookClient(base_url="https://api.example.com")
# All subsequent requests include: Authorization: Bearer SuperSecretProdPass
Manual Header Addition
For custom HTTP clients, add the header manually:
headers = {
"Authorization": "Bearer SuperSecretProdPass"
}
response = requests.get("https://api.example.com/api/notes", headers=headers)
Fine-Grained Route Protection (Optional)
While the middleware protects all routes globally, you can also use the check_api_password dependency from api/auth.py for fine-grained control within specific route handlers:
from fastapi import APIRouter, Depends, HTTPException
from api.auth import check_api_password
router = APIRouter()
@router.get("/admin/status")
async def admin_status(auth_ok: bool = Depends(check_api_password)):
if not auth_ok:
raise HTTPException(status_code=401, detail="Unauthorized")
return {"msg": "All systems nominal"}
This dependency is rarely necessary because the middleware already enforces authentication, but it is available for specialized access control logic.
Summary
- Enable authentication by setting
OPEN_NOTEBOOK_PASSWORDor mounting a file toOPEN_NOTEBOOK_PASSWORD_FILE; the middleware reads this at process start viaget_secret_from_envinopen_notebook/utils/encryption.py. - Restart required: The API does not hot-reload secrets; you must restart the container after changing the password.
- Public endpoints: Health checks and OpenAPI documentation remain accessible without authentication, as configured in
api/main.py. - Client compatibility: The provided
OpenNotebookClientinapi/client.pyautomatically handles header injection when the environment variable is present. - Request order: Authentication is enforced before CORS processing, ensuring rejected requests never trigger cross-origin logic.
Frequently Asked Questions
What happens if I don't set OPEN_NOTEBOOK_PASSWORD in production?
If the environment variable and secret file are both absent, PasswordAuthMiddleware allows all requests unrestricted access. This behavior is intended for local development only; production deployments must explicitly provide the secret to enable protection.
Can I use multiple passwords or user accounts with this middleware?
No, the current implementation in api/auth.py supports only a single master password. It is a simple shared-secret model rather than a multi-user authentication system. For multi-user scenarios, you would need to implement a custom authentication layer or place Open Notebook behind an authenticating reverse proxy.
How do I rotate the production password without downtime?
You must perform a rolling restart of your API containers. Because get_secret_from_env reads the password once at process startup (in open_notebook/utils/encryption.py), changes to the underlying secret file or environment variable are not detected until the process restarts. Update the secret, then restart containers one by one if running a cluster.
Are WebSocket connections also protected by this middleware?
Yes, the PasswordAuthMiddleware processes all HTTP requests, including WebSocket upgrade requests. Clients must include the Authorization: Bearer <password> header when establishing the WebSocket connection, or the middleware will reject the handshake before it reaches the application code.
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 →