/** * Edge-compatible auth helpers (for middleware). * Uses Web Crypto API instead of Node.js crypto. */ const COOKIE_NAME = "bh-admin-token"; function getSecret(): string { const secret = process.env.AUTH_SECRET; if (!secret) throw new Error("AUTH_SECRET is not set"); return secret; } function base64urlEncode(buf: ArrayBuffer): string { const bytes = new Uint8Array(buf); let binary = ""; for (const b of bytes) binary += String.fromCharCode(b); return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } async function hmacSign(data: string, secret: string): Promise { const encoder = new TextEncoder(); const key = await crypto.subtle.importKey( "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"] ); const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(data)); return base64urlEncode(sig); } export async function verifyToken(token: string): Promise { try { const [data, sig] = token.split("."); if (!data || !sig) return false; const expectedSig = await hmacSign(data, getSecret()); if (sig !== expectedSig) return false; const payload = JSON.parse(atob(data.replace(/-/g, "+").replace(/_/g, "/"))) as { role: string; exp: number; }; return payload.role === "admin" && payload.exp > Date.now(); } catch { return false; } } export { COOKIE_NAME };