/** * Edge-compatible session signer/verifier using Web Crypto API. */ async function getSessionKey() { const secret = process.env.CAPTCHA_SECRET || 'dev_secret_meow'; const encoder = new TextEncoder(); return await crypto.subtle.importKey( 'raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify'] ); } export async function signSession() { const expires = Date.now() + 24 * 60 * 60 * 1000; // 24 hours validity const data = `admin.${expires}`; const encoder = new TextEncoder(); const key = await getSessionKey(); const signatureBuffer = await crypto.subtle.sign('HMAC', key, encoder.encode(data)); const signatureArray = Array.from(new Uint8Array(signatureBuffer)); const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join(''); return `${data}.${signatureHex}`; } export async function verifySession(token) { if (!token) return false; const parts = token.split('.'); if (parts.length !== 3) return false; const [user, expires, signatureHex] = parts; if (user !== 'admin') return false; // Check if token expired if (Date.now() > parseInt(expires, 10)) return false; const data = `${user}.${expires}`; const encoder = new TextEncoder(); const key = await getSessionKey(); // Convert hex string back to Uint8Array const signatureBytes = new Uint8Array( signatureHex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)) ); try { // Verify the HMAC signature ensures the token hasn't been tampered with return await crypto.subtle.verify('HMAC', key, signatureBytes, encoder.encode(data)); } catch { return false; } }