fix(auth): make the Secure cookie flag fail-safe (default on)
secureCookies() keyed off NODE_ENV === "production", so an appliance deployed without that var silently sent the auth + CSRF cookies WITHOUT the Secure flag — the review's one Medium finding. Now Secure is the DEFAULT and you only ever opt OUT: a misconfigured/forgotten env can only make cookies more restrictive, never drop the flag. Dropped only on a deliberate COOKIE_SECURE=0/false/no/off (or an explicit NODE_ENV=development as a dev fallback). The LAN appliance that serves the SPA over plain http sets COOKIE_SECURE=0 on purpose (a Secure cookie would never be sent over its http origin and would lock operators out); a TLS deploy leaves it unset and gets Secure. - auth.test.ts (5): pins the matrix — default Secure, production Secure, dev opt-out, COOKIE_SECURE falsey opts out, any other value opts in. - .env.example documents COOKIE_SECURE (replaces the stale NODE_ENV cookie note). - dev .env sets COOKIE_SECURE=0 (local http://localhost login keeps working). server 80/80; build+lint green. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+22
-3
@@ -50,9 +50,28 @@ export function requireJwtSecret(): string {
|
||||
return secret;
|
||||
}
|
||||
|
||||
/** Cookies are secure in production; relaxed for local http dev. */
|
||||
function secureCookies(): boolean {
|
||||
return process.env.NODE_ENV === "production";
|
||||
/**
|
||||
* Whether to set the `Secure` flag on the auth/CSRF cookies. FAIL-SAFE: default is
|
||||
* `true` (Secure) — a misconfigured/forgotten env can only ever make cookies MORE
|
||||
* restrictive, never silently drop the flag.
|
||||
*
|
||||
* The previous gate keyed off `NODE_ENV === "production"`, which meant an appliance
|
||||
* deployed without that var leaked cookies over plain HTTP. Now `Secure` is the
|
||||
* default and is dropped ONLY for an explicit, deliberate opt-out — `COOKIE_SECURE`
|
||||
* set to a falsey value (`0/false/no/off`), or the legacy `NODE_ENV !== production`
|
||||
* signal kept as a fallback so existing dev setups still work over http://localhost.
|
||||
*
|
||||
* The parking appliance often serves the SPA same-origin over the LAN with no TLS;
|
||||
* THAT box sets `COOKIE_SECURE=0` on purpose (a Secure cookie would never be sent
|
||||
* over its http origin and would lock operators out). Everything else stays secure.
|
||||
*/
|
||||
export function secureCookies(): boolean {
|
||||
const override = process.env.COOKIE_SECURE;
|
||||
if (override !== undefined) {
|
||||
return !/^(0|false|no|off)$/i.test(override.trim());
|
||||
}
|
||||
// No explicit override: secure unless this is an obvious local-dev run.
|
||||
return process.env.NODE_ENV !== "development";
|
||||
}
|
||||
|
||||
export function newCsrfToken(): string {
|
||||
|
||||
Reference in New Issue
Block a user