feat: optional Bearer-token authentication via API_TOKEN env var
Disabled by default (empty API_TOKEN). When set: - All /api/* and /mcp requests require: Authorization: Bearer <token> - Public exemptions: /, /healthz, /static/*, /auth-check - Web UI: pre-flight /auth-check on load; shows token modal if required - Token stored in sessionStorage, sent on every API request - Mid-session 401s re-trigger the token modal - MCP clients must pass the header: Authorization: Bearer <token> Files changed: - app/config.py: api_token field + API_TOKEN env var - app/api/auth.py: Starlette BaseHTTPMiddleware for token enforcement - main.py: register middleware + /auth-check public endpoint - static/js/api.js: token storage, auth header, 401 handler hook - static/js/app.js: auth pre-flight, showTokenModal(), bootstrap() - static/css/components.css: .auth-overlay / .auth-card styles - README.md: API_TOKEN env var docs + MCP client header example
This commit is contained in:
@@ -33,6 +33,9 @@ class Settings:
|
||||
# Set MCP_STATELESS=false to use stateful sessions (needed for multi-turn MCP flows).
|
||||
mcp_stateless: bool = True
|
||||
|
||||
# Authentication — set API_TOKEN env var to enable; empty string disables auth entirely.
|
||||
api_token: str = ""
|
||||
|
||||
|
||||
def _parse_bool(value: str, default: bool) -> bool:
|
||||
if value.lower() in ("1", "true", "yes", "on"):
|
||||
@@ -54,6 +57,7 @@ def load_settings() -> Settings:
|
||||
agent_stale_after_seconds=int(os.getenv("AGENT_STALE_AFTER_SECONDS", "30")),
|
||||
mcp_server_name=os.getenv("MCP_SERVER_NAME", "local-mcp"),
|
||||
mcp_stateless=_parse_bool(os.getenv("MCP_STATELESS", "true"), default=True),
|
||||
api_token=os.getenv("API_TOKEN", ""),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user