/** * App-side signed user id cookie (backup gate). * Briven sets sAccessToken / sRefreshToken via the FDI proxy; SDK getSession * now uses engine /session/me through the first-party proxy. We still mint * krypco_uid after OTP/magic for a simple local dashboard gate. */ import { createHmac, timingSafeEqual } from "node:crypto"; export const KRYPCO_UID_COOKIE = "krypco_uid"; const MAX_AGE_SEC = 60 * 60 * 24 * 30; // 30 days function secret(): string { return ( process.env.KRYPCO_SESSION_SECRET?.trim() || process.env.BRIVEN_AUTH_PUBLIC_KEY?.trim() || process.env.NEXT_PUBLIC_BRIVEN_AUTH_KEY?.trim() || "krypco-dev-session-secret" ); } export function signUserId(userId: string): string { const uid = userId.trim(); const sig = createHmac("sha256", secret()).update(uid).digest("base64url"); return `${uid}.${sig}`; } export function verifyUserIdToken( token: string | undefined | null, ): string | null { if (!token || !token.includes(".")) return null; const i = token.lastIndexOf("."); const uid = token.slice(0, i); const sig = token.slice(i + 1); const expected = createHmac("sha256", secret()).update(uid).digest("base64url"); try { const a = Buffer.from(sig); const b = Buffer.from(expected); if (a.length !== b.length || !timingSafeEqual(a, b)) return null; } catch { return null; } return uid; } export function krypcoUidCookieOptions(secure: boolean) { return { httpOnly: true, secure, sameSite: "lax" as const, path: "/", maxAge: MAX_AGE_SEC, }; } export function readCookie( header: string | null, name: string, ): string | null { if (!header) return null; for (const part of header.split(";")) { const [k, ...rest] = part.trim().split("="); if (k === name) return rest.join("=") || null; } return null; }