import { createHash, timingSafeEqual } from "node:crypto"; import { db } from "~/utils/db.server"; function normalizePassword(password: string) { return password.trim(); } export function hashPassword(password: string) { const normalizedPassword = normalizePassword(password); return createHash("sha256").update(normalizedPassword).digest("hex"); } function isHashedPassword(value: string) { return /^[a-f0-9]{64}$/i.test(value); } export function verifyPassword(password: string, storedPassword: string | null | undefined) { if (!storedPassword) { return false; } const normalizedPassword = normalizePassword(password); const hashedPassword = hashPassword(normalizedPassword); if (isHashedPassword(storedPassword)) { const storedBuffer = Buffer.from(storedPassword); const hashedBuffer = Buffer.from(hashedPassword); if (hashedBuffer.length !== storedBuffer.length) { return false; } return timingSafeEqual(hashedBuffer, storedBuffer); } return normalizePassword(storedPassword) === normalizedPassword; } export async function validateCredentials( username: string, password: string ) { const user = await db.user.findUnique({ where: { username }, select: { id: true, password: true }, }); if (!user) { return null; } const passwordMatches = verifyPassword(password, user.password); if (!passwordMatches) { return null; } if (!isHashedPassword(user.password ?? "")) { await db.user.update({ where: { id: user.id }, data: { password: hashPassword(password) }, }); } return user.id; }