import { createHmac, timingSafeEqual } from "node:crypto"; export const recoveryCookie = "mg_password_recovery"; function signature(value: string, secret: string) { return createHmac("sha256", secret) .update("minegeo-password-recovery:" + value) .digest("hex"); } export function createRecoveryGrant( userId: string, sessionId: string, secret: string, now = Date.now(), ) { const value = `${userId}:${sessionId}:${Math.floor(now / 1000) + 600}`; return `${value}:${signature(value, secret)}`; } export function verifyRecoveryGrant( token: string | undefined, userId: string, sessionId: string, secret: string, now = Date.now(), ) { const parts = token?.split(":"); if ( !parts || parts.length !== 4 || parts[0] !== userId || parts[1] !== sessionId || !/^\d+$/.test(parts[2]) || !/^[0-9a-f]{64}$/.test(parts[3]) ) return false; const expires = Number(parts[2]), time = Math.floor(now / 1000); if (expires <= time || expires > time + 600) return false; return timingSafeEqual( Buffer.from(parts[3], "hex"), Buffer.from(signature(parts.slice(0, 3).join(":"), secret), "hex"), ); }