Files
EpicNext-Cms/src/lib/auth/password.ts
T

62 lines
1.3 KiB
TypeScript

import { randomBytes } from "node:crypto";
import {
bcrypt,
bcryptVerify,
md5,
} from "hash-wasm";
import { env } from "@/env";
export async function hashPassword(password: string): Promise<string> {
const h = await bcrypt({
password,
salt: randomBytes(16),
costFactor: env.BCRYPT_ROUNDS,
outputType: "encoded",
});
return h.replace(/^\$2[ab]\$/, "$2y$");
}
export async function md5Hex(input: string): Promise<string> {
return await md5(input);
}
export async function isMd5Of(
password: string,
stored: string,
): Promise<boolean> {
return (
/^[a-f0-9]{32}$/i.test(stored) &&
(await md5Hex(password)) === stored.toLowerCase()
);
}
export async function verifyPassword(
password: string,
stored: string,
): Promise<boolean> {
if (/^\$2[aby]\$/.test(stored)) {
try {
return await bcryptVerify({ password, hash: stored });
} catch {
return false;
}
}
return false;
}
export interface LoginCheck {
valid: boolean;
upgradedHash?: string;
}
export async function checkLogin(
password: string,
stored: string,
opts: { convertPasswords: boolean },
): Promise<LoginCheck> {
if (opts.convertPasswords && (await isMd5Of(password, stored))) {
return { valid: true, upgradedHash: await hashPassword(password) };
}
return { valid: await verifyPassword(password, stored) };
}