Pull Request Merged
PR #6 merged: feat: Admin authentication and dynamic OAuth URLs
Summary
SECURITY FIX: Previously the admin dashboard was completely open - anyone with the URL could access your health data. This PR adds proper authentication.
- Add admin account creation on first run
- Implement login/logout with server-side sessions
- Fix OAuth redirect_uri to use dynamic BASE_URL (no more localhost)
- Protect all admin routes behind authentication
Security Model
- Password hashing: Argon2 (winner of Password Hashing Competition)
- Sessions: Server-side with HTTP-only cookies (24hr expiry)
- Timing attacks: Constant-time password comparison
- First-run protection: Must create admin before accessing anything
Changes
| File | Purpose |
|---|---|
admin/auth.py | Auth logic, guards, session management |
core/password.py | Argon2 password hashing |
models/admin_user.py | AdminUser model |
templates/admin/login.html | Login form |
templates/admin/setup_account.html | First-run admin creation |
alembic/versions/*_add_admin_users.py | Migration |
Flow
/admin
├── No admin user? → /admin/setup/account (create admin)
├── Not logged in? → /admin/login
├── No OAuth setup? → /admin/setup (with dynamic callback URL)
└── All good → /admin/dashboard
Configuration
# Optional - auto-detected from request if not set
BASE_URL=https://polar-server.example.com
# Optional - auto-generated and persisted if not set
SESSION_SECRET=your-secret-key
Test plan
- First visit to /admin redirects to /admin/setup/account
- Cannot bypass account creation by visiting other /admin/* routes
- After creating account, redirects to OAuth setup
- Logout clears session and redirects to login
- OAuth callback uses correct URL (not localhost)
- Password validation (min 8 chars, confirmation match)
- Session expires after 24 hours
Breaking changes
None - existing deployments will be prompted to create admin account on first visit.
Generated with Claude Code
+1019
additions
-34
deletions
16
files changed