# HoffDesk Auth Unification **Date:** 2026-04-24 **Decision by:** Matt (Director) **Affects:** All `/admin/*` routes, dashboard, API consumers, webhooks --- ## The Decision Two auth patterns, no exceptions: | Consumer | Auth Method | How | |----------|-------------|-----| | **Browser (human)** | Session cookie | Login page → server sets `sessionid` cookie → validated on every request | | **API / Webhook / Bot** | Bearer token | `X-Hoffdesk-Secret` or `Authorization: Bearer ` header | **No query-param tokens. No meta-tag secrets. No mixed patterns.** --- ## Migration Map | Current Pattern | Route | Migrate To | |----------------|-------|------------| | `X-Admin-Token` header | `/admin/blog/*` | Session cookie (browser) or Bearer token (API) | | `?token=hoffdesk-admin-2025` query param | `/admin/content/generate*` | Session cookie (browser) or Bearer token (API) | | `X-Hoffdesk-Secret` header | `/family/events/removed` | Bearer token (it's a machine-to-machine call) | | Session login (no route) | `/admin/family/login` | Session cookie (wire the route, this was already the plan) | | Telegram secret | `/telegram/callback` | Keep as-is (internal bot webhook) | | Webhook secret | `/webhook` | Keep as-is (internal service webhook) | --- ## Implementation Plan ### Socrates (Backend) 1. **Add session middleware** to hoffdesk-api - Login endpoint: `POST /auth/login` — validates credentials, sets `sessionid` cookie - Logout endpoint: `POST /auth/logout` — clears cookie - Session middleware: checks `sessionid` cookie on all `/admin/*` routes - Token middleware: checks `Authorization: Bearer ` or `X-Hoffdesk-Secret` header on `/api/*` routes 2. **Unify admin auth** - `/admin/blog/*` → require session cookie OR Bearer token - `/admin/family/*` → require session cookie OR Bearer token - `/admin/content/*` → require session cookie OR Bearer token - Kill `?token=` query param pattern 3. **Keep webhook/bot auth as-is** - `/telegram/callback` → internal secret validation (unchanged) - `/webhook` → internal validation (unchanged) - `/family/events/removed` → Bearer token (machine-to-machine) ### Daedalus (Frontend) 1. **Blog admin** — replace `X-Admin-Token` HTMX interceptor with session cookie flow - Add login page redirect when 401 received - Remove `?token=` from all content generation URLs - HTMX handles cookies automatically (same-origin) 2. **Family dashboard** — use proxy pattern for removed-events - Dev server proxies `/family/events/removed` and injects auth header - No client-side secrets in production 3. **Login page** — `/admin/blog/login` and `/admin/family/login` share one template, one flow --- ## Credentials - **Admin session:** Matt logs in with username/password → `sessionid` cookie (httpOnly, Secure, SameSite=Strict) - **API tokens:** One `Hoffdesk-Secret` for internal services (webhooks, dashboard proxy), one `admin-token` for API consumers - **Aundrea's access:** She logs in once → session cookie → never sees a token --- ## What This Unblocks - Aundrea can access the family dashboard without seeing API secrets - All admin routes use the same login flow - API consumers (scripts, bots) use tokens - No more auth surprise when switching between blog admin and family admin