Skip to content

Fix #5: Add password-based admin authentication to dashboard#6

Merged
rekpero merged 2 commits into
mainfrom
fix/issue-5
Mar 15, 2026
Merged

Fix #5: Add password-based admin authentication to dashboard#6
rekpero merged 2 commits into
mainfrom
fix/issue-5

Conversation

@rekpero
Copy link
Copy Markdown
Owner

@rekpero rekpero commented Mar 15, 2026

Closes #5

Summary

  • Backend: Added HTTP middleware to FastAPI that protects all /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_digest used for timing-safe credential comparison.
  • Config: Added ADMIN_USERNAME (default: admin) and ADMIN_PASSWORD (required) env vars with validation warning in validate_environment() and display in print_config().
  • Database: Added sessions table with create_session, get_session, delete_session, cleanup_expired_sessions functions.
  • Frontend: API client injects Authorization: Bearer <token> header and dispatches swarm:unauthorized event on 401. New AuthContext manages token state. LoginPage shows a styled dark-theme login form. App.jsx gates rendering behind auth check. Header.jsx adds a logout button with LogOut icon.

Test plan

  • Set ADMIN_PASSWORD=testpassword123 in .env, restart server — print_config shows (set)
  • curl http://localhost:8420/api/metrics returns 401 without token
  • curl -X POST .../api/auth/login -d '{"username":"admin","password":"testpassword123"}' returns {"token":"...","expires_at":"..."}
  • Authenticated request with Authorization: Bearer <token> returns metrics JSON
  • Browser shows login page when unauthenticated; redirects to dashboard after login
  • Token persists across page reloads; dashboard loads without re-login
  • Server restart: existing localStorage token still valid (sessions in SQLite)
  • Logout button in header clears token and shows login page
  • Wrong password shows "Invalid username or password" error
  • Static assets (JS/CSS) load without auth token

- 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>
Copy link
Copy Markdown
Owner Author

@rekpero rekpero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Comment thread orchestrator/dashboard.py Outdated
Comment thread frontend/src/App.jsx Outdated
Comment thread frontend/src/api/client.js Outdated
- 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>
Copy link
Copy Markdown
Owner Author

@rekpero rekpero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 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.

@rekpero rekpero merged commit 96f7f8f into main Mar 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add password-based admin authentication to dashboard

1 participant