F1 — Login page + auth gate + session middleware #480

Closed
opened 2026-04-27 21:42:30 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator, I want the web app to require sign-in before anything is visible, so that anyone on the LAN cannot read or mutate the agent fleet without authenticating.

Acceptance criteria

  • apps/web/src/routes/login.tsx route renders three buttons: "Sign in with Forgejo", "Sign in with GitHub", "Sign in with GitLab". Each is an anchor to /oauth/<forge>/init?return=<current_url_or_/>.
  • Server-side session middleware on every web-app route and every operator-facing API (/board, /watchdog, /monitor, /agents, /flows, /forges*, /watched-repos*, …):
    • No / invalid / expired session cookie + Accept: text/html → 302 to /login.
    • Otherwise → 401 JSON { error: 'unauthenticated' }.
  • Webhook routes (/webhooks/*), the OAuth callback routes, and the static /login page are explicitly exempt from the gate.
  • operator_sessions table + getSession() / createSession({forge_type,account_login}) / deleteSession(id) / touchSession(id) helpers in db.ts.
  • Session cookie: 32-byte random, HttpOnly, Secure, SameSite=Lax, 30 d sliding TTL. Cookie name claude-hooks-session.
  • POST /logout deletes the row, clears the cookie, 302s to /login. Logout does NOT revoke the OAuth token.
  • Boot validation refuses to start if public_base_url is set but is not HTTPS — avoids cookies in clear.
  • Daily background tick clears expired operator_sessions rows.
  • Test: end-to-end gate — /board without cookie → 302 /login; with cookie → 200.

Out of scope

  • The OAuth providers themselves (F3 / F3-GH / F3-GL).
  • "Remember me" toggle.

References

  • Spec: docs/specs/forge-auth-repo-selection.md §F1, §5, §10

Dependencies

  • Blocked by F2 (operator_oauth_tokens + active-forge router)
As an operator, I want the web app to require sign-in before anything is visible, so that anyone on the LAN cannot read or mutate the agent fleet without authenticating. ## Acceptance criteria - [ ] `apps/web/src/routes/login.tsx` route renders three buttons: "Sign in with Forgejo", "Sign in with GitHub", "Sign in with GitLab". Each is an anchor to `/oauth/<forge>/init?return=<current_url_or_/>`. - [ ] Server-side **session middleware** on every web-app route and every operator-facing API (`/board`, `/watchdog`, `/monitor`, `/agents`, `/flows`, `/forges*`, `/watched-repos*`, …): - No / invalid / expired session cookie + `Accept: text/html` → 302 to `/login`. - Otherwise → 401 JSON `{ error: 'unauthenticated' }`. - [ ] Webhook routes (`/webhooks/*`), the OAuth callback routes, and the static `/login` page are explicitly exempt from the gate. - [ ] `operator_sessions` table + `getSession()` / `createSession({forge_type,account_login})` / `deleteSession(id)` / `touchSession(id)` helpers in `db.ts`. - [ ] Session cookie: 32-byte random, HttpOnly, Secure, SameSite=Lax, 30 d sliding TTL. Cookie name `claude-hooks-session`. - [ ] `POST /logout` deletes the row, clears the cookie, 302s to `/login`. Logout does NOT revoke the OAuth token. - [ ] Boot validation refuses to start if `public_base_url` is set but is not HTTPS — avoids cookies in clear. - [ ] Daily background tick clears expired `operator_sessions` rows. - [ ] Test: end-to-end gate — `/board` without cookie → 302 `/login`; with cookie → 200. ## Out of scope - The OAuth providers themselves (F3 / F3-GH / F3-GL). - "Remember me" toggle. ## References - Spec: `docs/specs/forge-auth-repo-selection.md` §F1, §5, §10 ## Dependencies - Blocked by F2 (`operator_oauth_tokens` + active-forge router)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#480
No description provided.