import type { CookieOptions } from 'express'; /** * Name of the transient cookie that carries the OIDC `state` and * PKCE `codeVerifier` between `/auth/login` and `/auth/callback`. * * v1 uses an unprefixed name because the local dev server is HTTP * and the `__Host-` prefix mandates `Secure`. Production hardening * (the dedicated infrastructure ADR for phase 3b) swaps this to * `__Host-portal_pre_auth` and turns on `Secure`. Until then the * exposure is limited to the 5-minute auth-flow window and the * cookie is `HttpOnly` + signed + `SameSite=Lax`. */ export const PRE_AUTH_COOKIE_NAME = 'portal_pre_auth'; /** * 5 minutes — enough for the round-trip through Entra (including a * fresh MFA prompt), short enough that a stale cookie can't be * replayed long after the user abandoned the flow. */ export const PRE_AUTH_COOKIE_TTL_MS = 5 * 60 * 1000; /** * Cookie options shared by the pre-auth cookie set (login) and the * pre-auth cookie clear (callback / failure paths). * * `signed: true` engages the `cookie-parser` signature using * `SESSION_SECRET`. `httpOnly: true` keeps the value out of any * JavaScript — the cookie is BFF-only. `sameSite: 'lax'` allows the * cookie to travel on Entra's cross-site top-level redirect back to * `/auth/callback`; a stricter `strict` would drop it. `secure` is * derived from `NODE_ENV` so prod gets the HTTPS-only flag without * breaking the local HTTP dev server. */ export function preAuthCookieOptions(): CookieOptions { const isProduction = process.env['NODE_ENV'] === 'production'; return { signed: true, httpOnly: true, sameSite: 'lax', secure: isProduction, path: '/', maxAge: PRE_AUTH_COOKIE_TTL_MS, }; }