refactor(auth): rip the sessionGateEnabled flag — gate is always on #537

Closed
opened 2026-04-28 15:43:08 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As an operator, I want session gating to be unconditional, so that misconfiguring public_base_url cannot leave the dashboard fully exposed and silent (loads → API returns nothing → empty screen, no login link).

Why

Today the gate has three logic layers fighting each other:

  1. sessionGateEnabled defaults to publicBaseUrl !== null (webhook-config.ts:1916) — so without public_base_url set, the gate auto-disables
  2. auth.session_gate_enabled boolean override exists for explicit on/off
  3. session-middleware.ts:207 short-circuits with if (!enabled) return next()

Combined effect on a desktop deployment with no public_base_url and no override: every request bypasses auth, the SPA mounts the dashboard, API calls return empty, the user sees a blank screen with no path to log in. Operator caught this on 2026-04-28 — /login was unreachable in practice because nothing redirected there.

The intended logic is plain: logged in → app, not logged in → login. No flag, no inference from public_base_url, no if (!enabled) skip.

Acceptance criteria

Code

  • apps/server/src/http/session-middleware.ts: drop the enabled parameter from makeSessionGateMiddleware. Middleware always enforces (exempt list still applies for /login, /oauth/*, /webhooks/*, /app/assets/*, etc.)
  • apps/server/src/shared/config/webhook-config.ts: remove the sessionGateEnabled field from WebhookConfig, the auth.session_gate_enabled schema entry, and the inference from publicBaseUrl
  • apps/server/src/main.ts: drop the cfg?.sessionGateEnabled === true branch; mount the gate unconditionally
  • apps/web/src/routes/__root.tsx (defense in depth): on mount call /auth/me (or whatever the canonical session-probe endpoint is); on 401 navigate to /login. Avoids reliance on server-side redirect alone

Config migration

  • If auth.session_gate_enabled is present in any deployed config/agents.json, the loader logs a deprecation warning at boot and ignores it
  • docs/specs/forge-auth-repo-selection.md (or whichever spec authored the flag) updated to remove the option

Tests

  • session-middleware.test.ts: drop the disabled-mode tests; add tests asserting that without a session cookie, every non-exempt path returns 302 to /login including /, /app, /agents, /board, etc.
  • SPA: test that __root navigates to /login when /auth/me returns 401

Local dev escape hatch (optional, only if needed)

  • If a true no-auth local dev mode is still wanted, expose it via env var only — e.g. CLAUDE_HOOKS_DISABLE_AUTH=1 — never via config file. Default is always on.

Out of scope

  • OAuth provider implementations themselves (#482, #483, GitLab equivalent)
  • Authelia integration changes
  • Session storage / cookie attribute changes

References

  • Current bug: dashboard reachable on a fresh deployment without OAuth credentials → blank screen, no login affordance
  • apps/server/src/http/session-middleware.ts:207
  • apps/server/src/shared/config/webhook-config.ts:1916
  • apps/web/src/routes/__root.tsx (no client-side check today)
## User story As an operator, I want session gating to be unconditional, so that misconfiguring `public_base_url` cannot leave the dashboard fully exposed and silent (loads → API returns nothing → empty screen, no login link). ## Why Today the gate has three logic layers fighting each other: 1. `sessionGateEnabled` defaults to `publicBaseUrl !== null` (`webhook-config.ts:1916`) — so without `public_base_url` set, the gate auto-disables 2. `auth.session_gate_enabled` boolean override exists for explicit on/off 3. `session-middleware.ts:207` short-circuits with `if (!enabled) return next()` Combined effect on a desktop deployment with no `public_base_url` and no override: every request bypasses auth, the SPA mounts the dashboard, API calls return empty, the user sees a blank screen with no path to log in. Operator caught this on 2026-04-28 — `/login` was unreachable in practice because nothing redirected there. The intended logic is plain: **logged in → app, not logged in → login**. No flag, no inference from `public_base_url`, no `if (!enabled)` skip. ## Acceptance criteria ### Code - [ ] `apps/server/src/http/session-middleware.ts`: drop the `enabled` parameter from `makeSessionGateMiddleware`. Middleware always enforces (exempt list still applies for `/login`, `/oauth/*`, `/webhooks/*`, `/app/assets/*`, etc.) - [ ] `apps/server/src/shared/config/webhook-config.ts`: remove the `sessionGateEnabled` field from `WebhookConfig`, the `auth.session_gate_enabled` schema entry, and the inference from `publicBaseUrl` - [ ] `apps/server/src/main.ts`: drop the `cfg?.sessionGateEnabled === true` branch; mount the gate unconditionally - [ ] `apps/web/src/routes/__root.tsx` (defense in depth): on mount call `/auth/me` (or whatever the canonical session-probe endpoint is); on 401 navigate to `/login`. Avoids reliance on server-side redirect alone ### Config migration - [ ] If `auth.session_gate_enabled` is present in any deployed `config/agents.json`, the loader logs a deprecation warning at boot and ignores it - [ ] `docs/specs/forge-auth-repo-selection.md` (or whichever spec authored the flag) updated to remove the option ### Tests - [ ] `session-middleware.test.ts`: drop the disabled-mode tests; add tests asserting that **without a session cookie, every non-exempt path returns 302 to `/login`** including `/`, `/app`, `/agents`, `/board`, etc. - [ ] SPA: test that `__root` navigates to `/login` when `/auth/me` returns 401 ### Local dev escape hatch (optional, only if needed) - [ ] If a true no-auth local dev mode is still wanted, expose it via env var only — e.g. `CLAUDE_HOOKS_DISABLE_AUTH=1` — never via config file. Default is always on. ## Out of scope - OAuth provider implementations themselves (#482, #483, GitLab equivalent) - Authelia integration changes - Session storage / cookie attribute changes ## References - Current bug: dashboard reachable on a fresh deployment without OAuth credentials → blank screen, no login affordance - `apps/server/src/http/session-middleware.ts:207` - `apps/server/src/shared/config/webhook-config.ts:1916` - `apps/web/src/routes/__root.tsx` (no client-side check today)
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#537
No description provided.