// Lightweight signed-session scaffold (HMAC-SHA256 via Web Crypto, no deps). // Production auth = Auth.js + Postgres adapter + OAuth (docs/02) — wired once the // database is provisioned. This provides the session shape + a guarded dev login. const enc = new TextEncoder(); export const SESSION_COOKIE = "0pt_session"; function b64url(buf: ArrayBuffer | Uint8Array): string { const b = buf instanceof Uint8Array ? buf : new Uint8Array(buf); let s = ""; for (const x of b) s += String.fromCharCode(x); return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } function fromB64url(s: string): Uint8Array { s = s.replace(/-/g, "+").replace(/_/g, "/"); s += "=".repeat((4 - (s.length % 4)) % 4); const bin = atob(s); const u = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) u[i] = bin.charCodeAt(i); return u; } async function hmacKey(): Promise { return crypto.subtle.importKey("raw", enc.encode(process.env.AUTH_SECRET ?? ""), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]); } export async function signSession(payload: Record): Promise { const body = b64url(enc.encode(JSON.stringify(payload))); const sig = await crypto.subtle.sign("HMAC", await hmacKey(), enc.encode(body)); return `${body}.${b64url(sig)}`; } export async function verifySession(token: string | undefined | null): Promise | null> { if (!token || !process.env.AUTH_SECRET) return null; const [body, sig] = token.split("."); if (!body || !sig) return null; const ok = await crypto.subtle.verify("HMAC", await hmacKey(), fromB64url(sig) as BufferSource, enc.encode(body)); if (!ok) return null; try { return JSON.parse(new TextDecoder().decode(fromB64url(body))); } catch { return null; } }