import { cookies } from "next/headers"; import { createHmac } from "crypto"; const SECRET = "sec-cybert-label-session-key"; const SESSION_COOKIE = "session"; const SESSION_MAX_AGE_MS = 8 * 60 * 60 * 1000; // 8 hours interface SessionPayload { annotatorId: string; createdAt: number; } function sign(payload: string): string { return createHmac("sha256", SECRET).update(payload).digest("hex"); } function encodeSession(payload: SessionPayload): string { const json = JSON.stringify(payload); const b64 = Buffer.from(json).toString("base64"); const signature = sign(b64); return `${b64}.${signature}`; } export function verifyAndDecode(raw: string): SessionPayload | null { const dotIndex = raw.lastIndexOf("."); if (dotIndex === -1) return null; const b64 = raw.slice(0, dotIndex); const signature = raw.slice(dotIndex + 1); const expected = sign(b64); if (signature !== expected) return null; try { const json = Buffer.from(b64, "base64").toString("utf-8"); const payload = JSON.parse(json) as SessionPayload; if (!payload.annotatorId || !payload.createdAt) return null; const age = Date.now() - payload.createdAt; if (age > SESSION_MAX_AGE_MS) return null; return payload; } catch { return null; } } export async function createSession(annotatorId: string): Promise { const cookieStore = await cookies(); const value = encodeSession({ annotatorId, createdAt: Date.now() }); cookieStore.set(SESSION_COOKIE, value, { httpOnly: true, sameSite: "lax", path: "/", maxAge: SESSION_MAX_AGE_MS / 1000, }); } export async function getSession(): Promise<{ annotatorId: string; createdAt: number } | null> { const cookieStore = await cookies(); const raw = cookieStore.get(SESSION_COOKIE)?.value; if (!raw) return null; const payload = verifyAndDecode(raw); if (!payload) return null; return { annotatorId: payload.annotatorId, createdAt: payload.createdAt }; } export async function destroySession(): Promise { const cookieStore = await cookies(); cookieStore.delete(SESSION_COOKIE); }