Spaces:
Sleeping
Sleeping
File size: 2,744 Bytes
57a889c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import { Request, Response } from 'express';
import { SESSION_DURATION_MS, SESSION_DURATION_REMEMBER_MS } from '../config';
const COOKIE_NAME = 'trek_session';
/**
* Controls the cookie lifetime for a login:
* - `undefined` β persistent `maxAge: SESSION_DURATION_MS` (the historical
* default, used by register/demo and anything that doesn't opt in).
* - `true` β persistent `maxAge: SESSION_DURATION_REMEMBER_MS` ("Remember me").
* - `false` β no `maxAge` β a browser-session cookie cleared on browser close.
*/
export type RememberOption = boolean | undefined;
/**
* Decide whether the session cookie should carry the `Secure` flag.
*
* We previously only derived this from `NODE_ENV=production` or
* `FORCE_HTTPS=true`. That left behind a common self-host setup:
* TREK running behind Traefik / Caddy / Cloudflare Tunnel with
* `NODE_ENV=development` locally and no `FORCE_HTTPS` β the cookie
* went out without `Secure`, even though the public leg was https.
*
* Now we also honour `req.secure`, which Express derives from
* `X-Forwarded-Proto` once `trust proxy` is set (TREK sets it to `1`
* in production automatically). If Express sees the request was TLS
* on the outermost hop, the cookie is `Secure`. `COOKIE_SECURE=false`
* remains the explicit escape hatch for plain-HTTP LAN testing.
*/
export function cookieOptions(clear = false, req?: Request, remember?: RememberOption) {
if (process.env.COOKIE_SECURE?.toLowerCase() === 'false') {
return buildOptions(clear, false, remember);
}
const envSecure = process.env.NODE_ENV?.toLowerCase() === 'production' || process.env.FORCE_HTTPS?.toLowerCase() === 'true';
const requestSecure = req?.secure === true;
return buildOptions(clear, envSecure || requestSecure, remember);
}
function resolveMaxAge(remember: RememberOption): { maxAge: number } | Record<string, never> {
// false β session cookie (omit maxAge); true β the longer "remember me"
// window; undefined β the historical default. Each maxAge matches the JWT exp.
if (remember === false) return {};
if (remember === true) return { maxAge: SESSION_DURATION_REMEMBER_MS };
return { maxAge: SESSION_DURATION_MS };
}
function buildOptions(clear: boolean, secure: boolean, remember?: RememberOption) {
return {
httpOnly: true,
secure,
sameSite: 'lax' as const,
path: '/',
...(clear ? {} : resolveMaxAge(remember)),
};
}
export function setAuthCookie(res: Response, token: string, req?: Request, remember?: RememberOption): void {
res.cookie(COOKIE_NAME, token, cookieOptions(false, req, remember));
}
export function clearAuthCookie(res: Response, req?: Request): void {
res.clearCookie(COOKIE_NAME, cookieOptions(true, req));
}
|