Files
Genarrative/server-node/src/auth/refreshSessionCookie.ts
2026-04-10 15:37:02 +08:00

97 lines
2.2 KiB
TypeScript

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,
};
}