perf: replace bcryptjs with native bcrypt for password hashing
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
830d252346
commit
d2120987b0
6 files changed
+47
-18
No files matched your search
@@ -1,9 +1,9 @@
|
||||
"use server";
|
||||
|
||||
import crypto from "node:crypto";
|
||||
import { hash } from "bcryptjs";
|
||||
import { z } from "zod";
|
||||
import { Prisma } from "@/generated/prisma/client";
|
||||
import { hashPassword } from "@/lib/auth/password";
|
||||
import { PERMS } from "@/lib/permissions";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { adminAction } from "@/lib/safe-action";
|
||||
@@ -30,7 +30,7 @@ export const createUser = adminAction(
|
||||
throw new ActionError("Cannot assign rank equal or higher than your own");
|
||||
}
|
||||
|
||||
const hashedPassword = await hash(password, 12);
|
||||
const hashedPassword = await hashPassword(password);
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
try {
|
||||
@@ -304,7 +304,7 @@ export const resetPassword = adminAction(
|
||||
.randomBytes(12)
|
||||
.toString("base64url")
|
||||
.slice(0, 16);
|
||||
const hashed = await hash(newPassword, 10);
|
||||
const hashed = await hashPassword(newPassword);
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: ctx.data.userId },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hash as bcryptHash } from "bcryptjs";
|
||||
import { hash as bcryptHash } from "bcrypt";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
checkLogin,
|
||||
@@ -50,9 +50,9 @@ describe("hashPassword (PASSWORD_HASH=argon2id)", () => {
|
||||
|
||||
describe("bcrypt", () => {
|
||||
it("verifies a bcrypt hash and accepts the PHP $2y$ prefix", async () => {
|
||||
const h = await bcryptHash("hunter2", 10); // bcryptjs emits $2a$
|
||||
const h = await bcryptHash("hunter2", 10); // native bcrypt emits $2a$/$2b$
|
||||
expect(await verifyPassword("hunter2", h)).toBe(true);
|
||||
// PHP stores $2y$ — bcryptjs must accept it as equivalent.
|
||||
// PHP stores $2y$ — bcrypt must accept it as equivalent.
|
||||
const phpStyle = h.replace(/^\$2[ab]\$/, "$2y$");
|
||||
expect(await verifyPassword("hunter2", phpStyle)).toBe(true);
|
||||
expect(await verifyPassword("nope", h)).toBe(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { compare as bcryptCompare, hash as bcryptHash } from "bcryptjs";
|
||||
import { compare as bcryptCompare, hash as bcryptHash } from "bcrypt";
|
||||
import { argon2id, argon2Verify, md5 } from "hash-wasm";
|
||||
|
||||
// AtomCMS hashing (config/hashing.php): argon2id with memory=65536 KiB, time=4,
|
||||
@@ -49,8 +49,8 @@ export async function hashPassword(password: string): Promise<string> {
|
||||
...ARGON2_PARAMS,
|
||||
});
|
||||
}
|
||||
// bcryptjs emits $2a$/$2b$; normalise to the PHP-canonical $2y$ the emulator
|
||||
// and existing AtomCMS rows use.
|
||||
// native bcrypt emits $2a$/$2b$; normalise to the PHP-canonical $2y$ the
|
||||
// emulator and existing AtomCMS rows use.
|
||||
const h = await bcryptHash(password, BCRYPT_ROUNDS);
|
||||
return h.replace(/^\$2[ab]\$/, "$2y$");
|
||||
}
|
||||
@@ -84,7 +84,9 @@ export async function verifyPassword(
|
||||
}
|
||||
if (/^\$2[aby]\$/.test(stored)) {
|
||||
try {
|
||||
return await bcryptCompare(password, stored);
|
||||
// PHP/AtomCMS store $2y$; native bcrypt only accepts $2a$/$2b$.
|
||||
const normalized = stored.replace(/^\$2y\$/, "$2a$");
|
||||
return await bcryptCompare(password, normalized);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user