Auth Unification - Daedalus Handoff
Date: 2026-04-24
Status: Backend complete, frontend changes required
Test URL: http://100.71.120.12:8000 (or notes.hoffdesk.com when tunnel is fixed)
What Changed
Session-based authentication is now live. No more tokens in URLs or headers for browser traffic.
New Auth Flow
User visits /admin/blog/
↓
No session cookie? → Redirect to /auth/login?redirect=/admin/blog/
↓
User enters credentials → Session cookie set (30 days)
↓
Auto-redirect back to original page
↓
All subsequent requests include session cookie automatically
Your Action Items
1. Remove X-Admin-Token Header (HTMX Interceptor)
File: Your HTMX interceptor (wherever you set X-Admin-Token: hoffdesk-admin-2025)
Before:
// Remove this entire block
htmx.on('htmx:configRequest', (evt) => {
evt.detail.headers['X-Admin-Token'] = 'hoffdesk-admin-2025';
});
After:
// HTMX automatically sends cookies on same-origin requests
// No code needed - cookies are automatic
2. Remove ?token= Query Parameters
Search for: ?token=hoffdesk-admin-2025 in your templates/JS
Before:
<a href="/admin/content/generate?token=hoffdesk-admin-2025">
After:
<a href="/admin/content/generate">
<!-- Cookie handles auth automatically -->
3. Handle 401 Responses
Add to your HTMX config:
htmx.on('htmx:responseError', (evt) => {
if (evt.detail.xhr.status === 401) {
window.location.href = '/auth/login?redirect=' + encodeURIComponent(window.location.pathname);
}
});
4. Login Page Template (Optional)
The backend provides /auth/login with a styled form. If you want to customize it, the template is at:
- Backend: auth/router.py → login_page() function (inline HTML)
To override: Create your own login template and have the backend redirect to it, or modify the inline HTML in auth/router.py.
Test Credentials
| User | Password | Access |
|---|---|---|
matt |
hoffdesk-matt-2026 |
admin, editor (full) |
aundrea |
hoffdesk-aundrea-2026 |
family (family dashboard only) |
Login endpoint: POST /auth/login
Logout: GET /auth/logout or POST /auth/logout
Magic Wand Fix
Problem: Content generation fetch doesn't include auth token.
Solution: Since cookies are automatic, just ensure fetch is same-origin:
// Before (broken - no auth)
fetch('/admin/content/generate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
// After (works - cookies automatic)
fetch('/admin/content/generate', {
method: 'POST',
credentials: 'same-origin', // Include this
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
If the Magic Wand uses vanilla JS fetch (not HTMX), add credentials: 'same-origin'.
API Routes (Unchanged)
Machine-to-machine routes still use Bearer tokens:
# Webhooks, Telegram callbacks
Authorization: Bearer <token>
# or
X-Hoffdesk-Secret: <token>
These routes were NOT changed:
- /webhook (Cloudflare Worker)
- /telegram/callback (Bot webhook)
- /family/events/removed (if called server-to-server)
Testing Checklist
# 1. Login
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"matt","password":"hoffdesk-matt-2026"}' \
-c cookies.txt
# 2. Access protected route
curl http://localhost:8000/admin/blog/ \
-b cookies.txt
# 3. Check session
curl http://localhost:8000/auth/me -b cookies.txt
# → {"authenticated":true,"user_id":"matt",...}
# 4. Logout
curl http://localhost:8000/auth/logout -b cookies.txt -c cookies.txt
Session Cookie Details
- Name:
hoffdesk_session - HttpOnly: Yes (JS can't read it, prevents XSS)
- SameSite: Lax (CSRF protection)
- Max-Age: 30 days
- Path:
/
Questions?
Ask Socrates (backend) or check shared/session_auth.py for the auth implementation.
Key files:
- main.py — Middleware wiring
- auth/router.py — Login/logout endpoints
- shared/session_auth.py — Session + Bearer validation
- shared/project-docs/auth-unification.md — Full spec
Ready for frontend implementation. 🎨