97 lines
2.2 KiB
TypeScript
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,
|
|
};
|
|
}
|