import { timingSafeEqual, randomBytes, scryptSync } from 'node:crypto'; const PREFIX = 'scrypt'; const KEY_LENGTH = 64; function hashWithSalt(password: string, salt: string): string { return scryptSync(password, salt, KEY_LENGTH).toString('base64'); } export function isPasswordHash(value: string): boolean { return value.startsWith(`${PREFIX}$`); } export function hashPassword(password: string): string { const salt = randomBytes(16).toString('base64'); const hash = hashWithSalt(password, salt); return `${PREFIX}$${salt}$${hash}`; } export function verifyPassword(password: string, stored: string): boolean { if (!stored) return true; if (!isPasswordHash(stored)) return password === stored; const [, salt, expected] = stored.split('$'); if (!salt || !expected) return false; const actual = hashWithSalt(password, salt); const expectedBytes = Buffer.from(expected, 'base64'); const actualBytes = Buffer.from(actual, 'base64'); if (expectedBytes.length !== actualBytes.length) return false; return timingSafeEqual(expectedBytes, actualBytes); }