39 lines
1.2 KiB
TypeScript
39 lines
1.2 KiB
TypeScript
/**
|
|
* Dashboard session cookies (JWT in an httpOnly cookie). No cookie
|
|
* library — set via Set-Cookie header, read by parsing the Cookie header.
|
|
*/
|
|
|
|
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
import { signJwt, verifyJwt } from './crypto.js';
|
|
import { config } from '../config.js';
|
|
|
|
const COOKIE = 'wpide_session';
|
|
|
|
export function setSession(reply: FastifyReply, userId: string): void {
|
|
const token = signJwt({ sub: userId });
|
|
const secure = config.PUBLIC_BASE_URL.startsWith('https');
|
|
const parts = [
|
|
`${COOKIE}=${token}`,
|
|
'Path=/',
|
|
'HttpOnly',
|
|
'SameSite=Lax',
|
|
`Max-Age=${60 * 60 * 24 * 30}`,
|
|
];
|
|
if (secure) parts.push('Secure');
|
|
reply.header('set-cookie', parts.join('; '));
|
|
}
|
|
|
|
export function clearSession(reply: FastifyReply): void {
|
|
reply.header('set-cookie', `${COOKIE}=; Path=/; HttpOnly; Max-Age=0`);
|
|
}
|
|
|
|
export function getSessionUserId(req: FastifyRequest): string | null {
|
|
const raw = req.headers.cookie;
|
|
if (!raw) return null;
|
|
const match = raw.split(';').map((c) => c.trim()).find((c) => c.startsWith(`${COOKIE}=`));
|
|
if (!match) return null;
|
|
const token = match.slice(COOKIE.length + 1);
|
|
const payload = verifyJwt<{ sub: string }>(token);
|
|
return payload?.sub ?? null;
|
|
}
|