mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-23 15:21:46 +00:00
feat(auth): add "Remember me" checkbox to extend session lifetime (#1189)
Adds a "Remember me" checkbox to the login form (single responsive page, covers mobile + desktop). Unchecked (default) issues the existing SESSION_DURATION JWT with a browser-session cookie (no maxAge); checked issues a longer-lived JWT plus a persistent cookie sized by the new SESSION_DURATION_REMEMBER env var (default 30d). The choice is threaded through the MFA verify leg so it survives the step-up. Register/demo logins keep their current persistent behaviour.
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { SESSION_DURATION_MS } from '../config';
|
||||
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.
|
||||
*
|
||||
@@ -18,27 +27,35 @@ const COOKIE_NAME = 'trek_session';
|
||||
* 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) {
|
||||
export function cookieOptions(clear = false, req?: Request, remember?: RememberOption) {
|
||||
if (process.env.COOKIE_SECURE?.toLowerCase() === 'false') {
|
||||
return buildOptions(clear, 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);
|
||||
return buildOptions(clear, envSecure || requestSecure, remember);
|
||||
}
|
||||
|
||||
function buildOptions(clear: boolean, secure: boolean) {
|
||||
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 ? {} : { maxAge: SESSION_DURATION_MS }), // matches the JWT expiry (SESSION_DURATION)
|
||||
...(clear ? {} : resolveMaxAge(remember)),
|
||||
};
|
||||
}
|
||||
|
||||
export function setAuthCookie(res: Response, token: string, req?: Request): void {
|
||||
res.cookie(COOKIE_NAME, token, cookieOptions(false, req));
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user