import { createHash, randomBytes } from 'crypto'; /** * MFA recovery codes. * * GoTrue has no native recovery-code support, so we manage them ourselves in * the `mfa_recovery_codes` table (service-role only; see migration 0002). We * only ever persist a SHA-256 hash of each code — the plaintext is shown to the * user exactly once at generation time. Codes are high-entropy random values, * so a fast hash is sufficient (no need for bcrypt/per-code salt). */ /** Normalise user input so hyphens / case / spacing don't matter on redeem. */ function normalize(code: string): string { return code.replace(/[^a-z0-9]/gi, '').toLowerCase(); } /** Stable hash used both when storing and when redeeming a code. */ export function hashRecoveryCode(code: string): string { return createHash('sha256').update(normalize(code)).digest('hex'); } /** Generate `n` formatted recovery codes (e.g. `a1b2-c3d4-e5f6`). */ export function generateRecoveryCodes(n = 10): string[] { const codes: string[] = []; for (let i = 0; i < n; i++) { const raw = randomBytes(6).toString('hex'); // 12 hex chars codes.push(`${raw.slice(0, 4)}-${raw.slice(4, 8)}-${raw.slice(8, 12)}`); } return codes; }