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:
@@ -480,3 +480,83 @@
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ── Auth overlay ─────────────────────────────────────────────────────────── */
|
||||
|
||||
.auth-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.72);
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
margin: var(--space-4);
|
||||
padding: var(--space-8);
|
||||
background: var(--surface-raised);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: 0 32px 64px rgba(0, 0, 0, 0.48);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.auth-card__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
background: color-mix(in srgb, var(--cyan) 10%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--cyan) 25%, transparent);
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
.auth-card__title {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.auth-card__desc {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.auth-card__desc code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9em;
|
||||
color: var(--cyan);
|
||||
background: color-mix(in srgb, var(--cyan) 10%, transparent);
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.auth-card__form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.auth-card__error {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--red);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user