diff --git a/src/lib/auth/password.test.ts b/src/lib/auth/password.test.ts index 7a78ee00..2125d8be 100644 --- a/src/lib/auth/password.test.ts +++ b/src/lib/auth/password.test.ts @@ -58,7 +58,8 @@ 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); // native bcrypt emits $2a$/$2b$ + const rounds = Number(process.env.BCRYPT_ROUNDS) || 4; + const h = await bcryptHash("hunter2", rounds); // native bcrypt emits $2a$/$2b$ expect(await verifyPassword("hunter2", h)).toBe(true); // PHP stores $2y$ — bcrypt must accept it as equivalent. const phpStyle = h.replace(/^\$2[ab]\$/, "$2y$"); diff --git a/src/lib/auth/password.ts b/src/lib/auth/password.ts index 39b0edf7..2d463f5d 100644 --- a/src/lib/auth/password.ts +++ b/src/lib/auth/password.ts @@ -11,7 +11,9 @@ const ARGON2_PARAMS = { memorySize: Number(process.env.ARGON2_MEMORY_SIZE) || 65536, // KiB hashLength: 32, } as const; -const BCRYPT_ROUNDS = 12; +function bcryptRounds(): number { + return Number(process.env.BCRYPT_ROUNDS) || 12; +} // Which algorithm hashPassword() emits for NEW/upgraded passwords. // - "bcrypt" (DEFAULT): 60-char $2y$ hash. Fits varchar(255) users.password. @@ -51,7 +53,7 @@ export async function hashPassword(password: string): Promise { } // 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); + const h = await bcryptHash(password, bcryptRounds()); return h.replace(/^\$2[ab]\$/, "$2y$"); }