Fix #5: Add password-based admin authentication to dashboard#6
Conversation
- Add ADMIN_USERNAME/ADMIN_PASSWORD config vars with validation in config.py - Add sessions table + create/get/delete/cleanup functions in db.py - Add HTTP middleware to protect all /api/* routes (except /api/auth/login) in dashboard.py - Add /api/auth/login, /api/auth/check, /api/auth/logout endpoints - Update .env.example with ADMIN_USERNAME and ADMIN_PASSWORD vars - Update frontend API client with Bearer token injection and 401 handling - Create AuthContext with AuthProvider and useAuth hook - Create LoginPage component with dark-theme styled form - Wrap app with AuthProvider in main.jsx - Add auth gate (isChecking/isAuthenticated) in App.jsx - Add LogOut button to Header.jsx Sessions stored in SQLite (30-day expiry), timing-safe credential comparison via secrets.compare_digest, localStorage token persistence for SPA reloads. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rekpero
left a comment
There was a problem hiding this comment.
🔴 Claude BugBot Analysis
Found 3 potential bugs in this PR.
medium: 2 | low: 1
Three bugs were found: a timing side-channel in the login endpoint due to short-circuit evaluation undermining secrets.compare_digest; unauthenticated data-fetching hooks that fire 401-cascading API requests when the user is not yet authenticated; and the swarm:unauthorized event being dispatched for all 401 responses including failed login attempts, conflating credential failure with session expiry.
- dashboard.py: evaluate username and password comparisons unconditionally before combining to prevent timing side-channel via short-circuit evaluation - client.js: only dispatch swarm:unauthorized and clear token when a token was actually sent, avoiding conflation of session expiry with bad credentials - useMetrics/useAgents/useIssues/usePRs: add enabled option; App.jsx passes enabled: isAuthenticated && !isChecking to prevent 401 cascade before auth is confirmed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rekpero
left a comment
There was a problem hiding this comment.
🟢 Claude BugBot Analysis
All three previously reported bugs have been fixed: the constant-time comparison now evaluates both digests unconditionally, the data-fetching hooks are gated by enabled: queryEnabled, and the swarm:unauthorized event is only dispatched when a token was actually sent. No new bugs were found in the added/modified lines.
No bugs were detected in this PR.
Closes #5
Summary
/api/*routes with session token auth. Login/logout/check endpoints added at/api/auth/*. Sessions stored in SQLite with 30-day expiry and cleaned up on new login.secrets.compare_digestused for timing-safe credential comparison.ADMIN_USERNAME(default:admin) andADMIN_PASSWORD(required) env vars with validation warning invalidate_environment()and display inprint_config().sessionstable withcreate_session,get_session,delete_session,cleanup_expired_sessionsfunctions.Authorization: Bearer <token>header and dispatchesswarm:unauthorizedevent on 401. NewAuthContextmanages token state.LoginPageshows a styled dark-theme login form.App.jsxgates rendering behind auth check.Header.jsxadds a logout button withLogOuticon.Test plan
ADMIN_PASSWORD=testpassword123in.env, restart server —print_configshows(set)curl http://localhost:8420/api/metricsreturns 401 without tokencurl -X POST .../api/auth/login -d '{"username":"admin","password":"testpassword123"}'returns{"token":"...","expires_at":"..."}Authorization: Bearer <token>returns metrics JSON