import crypto from 'node:crypto'; import type { Request, Response } from 'express'; import type { AppConfig } from '../config.js'; export type RefreshSessionRequestContext = { clientType: string; userAgent: string | null; ip: string | null; }; function buildCookieParts( config: AppConfig, value: string, options: { maxAgeSeconds: number; }, ) { const parts = [ `${config.authSession.refreshCookieName}=${encodeURIComponent(value)}`, `Path=${config.authSession.refreshCookiePath}`, 'HttpOnly', `SameSite=${config.authSession.refreshCookieSameSite}`, `Max-Age=${Math.max(0, Math.floor(options.maxAgeSeconds))}`, ]; if (config.authSession.refreshCookieSecure) { parts.push('Secure'); } return parts.join('; '); } export function hashRefreshSessionToken(token: string) { return crypto.createHash('sha256').update(token).digest('hex'); } export function createRefreshSessionToken() { return crypto.randomBytes(32).toString('base64url'); } export function setRefreshSessionCookie( response: Response, config: AppConfig, token: string, maxAgeSeconds: number, ) { response.setHeader( 'Set-Cookie', buildCookieParts(config, token, { maxAgeSeconds, }), ); } export function clearRefreshSessionCookie(response: Response, config: AppConfig) { response.setHeader( 'Set-Cookie', buildCookieParts(config, '', { maxAgeSeconds: 0, }), ); } export function readRefreshSessionToken(request: Request, config: AppConfig) { const cookieHeader = request.header('cookie')?.trim() || ''; if (!cookieHeader) { return ''; } const cookieEntries = cookieHeader.split(';'); for (const entry of cookieEntries) { const [rawName, ...valueParts] = entry.split('='); const name = rawName?.trim(); if (name !== config.authSession.refreshCookieName) { continue; } const rawValue = valueParts.join('=').trim(); return rawValue ? decodeURIComponent(rawValue) : ''; } return ''; } export function buildRefreshSessionRequestContext( request: Request, ): RefreshSessionRequestContext { const userAgent = request.header('user-agent')?.trim() || null; return { clientType: 'browser', userAgent, ip: request.ip || null, }; }