32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
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;
|
|
}
|