diff --git a/docs/superpowers/specs/2026-05-27-auth-system-design.md b/docs/superpowers/specs/2026-05-27-auth-system-design.md index 20ca23e..0ed6d6d 100644 --- a/docs/superpowers/specs/2026-05-27-auth-system-design.md +++ b/docs/superpowers/specs/2026-05-27-auth-system-design.md @@ -71,9 +71,11 @@ Nothing else counts. No `localStorage` flags, no JWT, no client-side "I think I' ```js export async function requireAuth(req, res, next) { - // Dev mode preserved + // Dev mode preserved. The 'dev' user is a real row in `users` seeded at + // boot when AUTH_ENABLED !== 'true', so FK-bearing routes (api_tokens, + // future comments, audit fields) keep working without conditional logic. if (process.env.AUTH_ENABLED !== 'true') { - req.user = { id: 'dev', username: 'dev' }; + req.user = DEV_USER; // { id: , username: 'dev' } return next(); } @@ -108,7 +110,18 @@ export async function requireAuth(req, res, next) { } ``` -Mounted at the `/api/v1` level in `services/mam-api/src/index.js`, with an allowlist for `/api/v1/auth/login`, `/api/v1/auth/setup`, `/api/v1/auth/setup-required`, and `/health`. +Mounted at the `/api/v1` level in `services/mam-api/src/index.js`, **before** the individual route mounts, with an allowlist for the three pre-login auth paths: + +```js +app.use('/api/v1', (req, res, next) => { + const unauth = ['/auth/login', '/auth/setup', '/auth/setup-required']; + if (unauth.some(p => req.path === p)) return next(); + return requireAuth(req, res, next); +}); +// then: app.use('/api/v1/assets', assetsRouter), etc. +``` + +`/health` lives at the root, outside the `/api/v1` mount, so it's naturally unaffected. `/api/v1/cluster/*` keeps its existing `019-node-token-binding` service-auth path: requireAuth runs first, fails with 401 for an unauthenticated request, **but** the cluster routes themselves do their own token check on request bodies, so node-agent traffic must include a valid user session OR an api_token (which is the change — node-agent will need to be issued an api_token at install time). Alternative: carve `/api/v1/cluster/*` out of the requireAuth gate too, and keep node-agent on its existing binding token alone. Implementer should pick — flagged in the implementation order. ### Session middleware (actually wired this time) @@ -236,11 +249,11 @@ Bearer tokens have their own optional `expires_at` (`NULL` = never expires); che Suggested sequencing for the implementation plan (writing-plans will refine): -1. Migration `023-auth-session-timestamps.sql`. +1. Migration `023-auth-session-timestamps.sql`. Add idempotent seed of the dev user (`INSERT ... ON CONFLICT DO NOTHING` with a fixed UUID) so dev mode FK-bearing routes work out of the box. 2. `express-session` + `connect-pg-simple` wiring in `index.js`. -3. `requireAuth` middleware. +3. `requireAuth` middleware (with `DEV_USER` constant resolved from the seeded row). 4. Auth router (setup, login, logout, me, password). -5. Apply `requireAuth` to API router with allowlist. +5. Apply `requireAuth` to API router with allowlist. Decide cluster carve-out (see Architecture). 6. Auth tests (unit + integration + regression). 7. Frontend `` + Login screen + Setup screen. 8. Frontend Settings -> Account + API Tokens.