import type { Request, Response } from 'express' import type { DbSchema, DbSession, SessionKind } from './db.js' import { newId, nowIso, pruneExpiredSessions } from './db.js' export const COOKIE_FULL = 'sid' export const COOKIE_PENDING = 'psid' function addMinutes(date: Date, minutes: number) { return new Date(date.getTime() + minutes * 60_000) } function addDays(date: Date, days: number) { return new Date(date.getTime() + days * 86_400_000) } export function createSession(db: DbSchema, userId: string, kind: SessionKind): DbSession { pruneExpiredSessions(db) const now = new Date() const expires = kind === 'mfa_pending' ? addMinutes(now, 10) : addDays(now, 7) const s: DbSession = { id: newId(), user_id: userId, kind, created_at: nowIso(), expires_at: expires.toISOString(), } db.sessions.push(s) return s } function isSecureRequest(req: Request) { if (req.secure) return true const xfp = req.headers['x-forwarded-proto'] if (typeof xfp === 'string') return xfp.split(',')[0]?.trim() === 'https' return false } function cookieBaseOptions(req: Request) { return { httpOnly: true, sameSite: 'lax' as const, secure: isSecureRequest(req), } } export function clearSessionCookies(req: Request, res: Response) { const base = cookieBaseOptions(req) res.clearCookie(COOKIE_FULL, base) res.clearCookie(COOKIE_PENDING, base) } export function setSessionCookie(req: Request, res: Response, session: DbSession) { const name = session.kind === 'mfa_pending' ? COOKIE_PENDING : COOKIE_FULL const base = cookieBaseOptions(req) res.cookie(name, session.id, { ...base, expires: new Date(session.expires_at), }) } export function getSessionFromRequest(req: Request, kind: SessionKind) { const cookies = (req as unknown as { cookies?: Record }).cookies const raw = cookies?.[kind === 'mfa_pending' ? COOKIE_PENDING : COOKIE_FULL] return typeof raw === 'string' ? raw : null } export function removeSessionsByIds(db: DbSchema, ids: string[]) { if (ids.length === 0) return const set = new Set(ids) db.sessions = db.sessions.filter((s) => !set.has(s.id)) } export function removeOtherUserSessions(db: DbSchema, userId: string, keepSessionIds: string[]) { const keep = new Set(keepSessionIds) db.sessions = db.sessions.filter((s) => s.user_id !== userId || keep.has(s.id)) }