session-cookie.ts 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * App-side signed user id cookie (backup gate).
  3. * Briven sets sAccessToken / sRefreshToken via the FDI proxy; SDK getSession
  4. * now uses engine /session/me through the first-party proxy. We still mint
  5. * krypco_uid after OTP/magic for a simple local dashboard gate.
  6. */
  7. import { createHmac, timingSafeEqual } from "node:crypto";
  8. export const KRYPCO_UID_COOKIE = "krypco_uid";
  9. const MAX_AGE_SEC = 60 * 60 * 24 * 30; // 30 days
  10. function secret(): string {
  11. return (
  12. process.env.KRYPCO_SESSION_SECRET?.trim() ||
  13. process.env.BRIVEN_AUTH_PUBLIC_KEY?.trim() ||
  14. process.env.NEXT_PUBLIC_BRIVEN_AUTH_KEY?.trim() ||
  15. "krypco-dev-session-secret"
  16. );
  17. }
  18. export function signUserId(userId: string): string {
  19. const uid = userId.trim();
  20. const sig = createHmac("sha256", secret()).update(uid).digest("base64url");
  21. return `${uid}.${sig}`;
  22. }
  23. export function verifyUserIdToken(
  24. token: string | undefined | null,
  25. ): string | null {
  26. if (!token || !token.includes(".")) return null;
  27. const i = token.lastIndexOf(".");
  28. const uid = token.slice(0, i);
  29. const sig = token.slice(i + 1);
  30. const expected = createHmac("sha256", secret()).update(uid).digest("base64url");
  31. try {
  32. const a = Buffer.from(sig);
  33. const b = Buffer.from(expected);
  34. if (a.length !== b.length || !timingSafeEqual(a, b)) return null;
  35. } catch {
  36. return null;
  37. }
  38. return uid;
  39. }
  40. export function krypcoUidCookieOptions(secure: boolean) {
  41. return {
  42. httpOnly: true,
  43. secure,
  44. sameSite: "lax" as const,
  45. path: "/",
  46. maxAge: MAX_AGE_SEC,
  47. };
  48. }
  49. export function readCookie(
  50. header: string | null,
  51. name: string,
  52. ): string | null {
  53. if (!header) return null;
  54. for (const part of header.split(";")) {
  55. const [k, ...rest] = part.trim().split("=");
  56. if (k === name) return rest.join("=") || null;
  57. }
  58. return null;
  59. }