harden(auth/cookies): __Host- prefix on all three auth cookies (MED-14, BREAKING)

Audit 2026-05-10 — close MED-14 from the HANDOFF.md backend batch
(item 5). The session, CSRF, and OIDC pre-login cookies all carry
the __Host- prefix; browsers now reject any subdomain attempt to
overwrite them.

Cookie name changes (BREAKING — existing sessions invalidate):
  - certctl_session       → __Host-certctl_session
  - certctl_csrf          → __Host-certctl_csrf
  - certctl_oidc_pending  → __Host-certctl_oidc_pending

The __Host- prefix requires Path=/ + Secure + no Domain attribute.
Post-login session + CSRF cookies already met all three. The pre-login
cookie's Path widened from '/auth/oidc/' to '/' to satisfy the prefix;
the cookie lives 10 minutes and is only consumed by the callback
handler, so the wider path scope is harmless.

Files touched:
  - internal/auth/session/domain/types.go — constant rename + comment
  - internal/auth/session/domain/types_test.go — assertion update
  - internal/api/handler/auth_session_oidc.go — pre-login set + clear
    paths widened from /auth/oidc/ to /
  - web/src/api/client.ts — readCSRFCookie now compares against
    '__Host-certctl_csrf'
  - CHANGELOG.md — Unreleased > Security (BREAKING) entry
  - docs/migration/oidc-enable.md — operator-facing detail of the
    one-time re-authentication window + GUI customization guidance

Operator impact: ONE re-login prompt per active session at the deploy
that lands this change. Subsequent logins issue the __Host-prefixed
cookie automatically. Existing bookmarked deep links work without
modification (cookies are path-scoped, not URL-scoped).

Refs: cowork/auth-bundles-fixes-2026-05-10/HANDOFF.md item 5
      cowork/auth-bundles-audit-2026-05-10.md MED-14
This commit is contained in:
shankar0123
2026-05-10 22:52:53 +00:00
parent 551812b2ca
commit 020bba35f0
6 changed files with 100 additions and 22 deletions
+10 -6
View File
@@ -55,17 +55,21 @@ function authHeaders(): Record<string, string> {
return headers;
}
// Bundle 2 Phase 8 — read the certctl_csrf cookie value (set by the
// OIDC-callback / break-glass-login flows; JS-readable by design so
// the GUI can echo it into the X-CSRF-Token header on every state-
// changing request). Returns empty string when the cookie isn't set
// (Bearer-mode deployments don't need CSRF; the server's middleware
// Bundle 2 Phase 8 — read the __Host-certctl_csrf cookie value (set
// by the OIDC-callback / break-glass-login flows; JS-readable by
// design so the GUI can echo it into the X-CSRF-Token header on every
// state-changing request). Returns empty string when the cookie isn't
// set (Bearer-mode deployments don't need CSRF; the server's middleware
// short-circuits CSRF for Bearer-authenticated requests).
//
// Audit 2026-05-10 MED-14 — cookie name carries the `__Host-` prefix
// (subdomain-takeover defense). The browser includes the prefix in
// document.cookie verbatim; the comparison below matches that.
function readCSRFCookie(): string {
if (typeof document === 'undefined' || !document.cookie) return '';
for (const part of document.cookie.split(';')) {
const [k, ...rest] = part.trim().split('=');
if (k === 'certctl_csrf') {
if (k === '__Host-certctl_csrf') {
return decodeURIComponent(rest.join('='));
}
}