fix: [ Aegon fix]
This commit is contained in:
1 parent
738d7b8223
commit
5b9e2166df
2 files changed
+40
-18
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { hash as bcryptHash } from "@node-rs/argon2";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { argon2id } from "hash-wasm";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mockEnv = vi.hoisted(() => ({
|
||||
@@ -29,10 +30,10 @@ describe("md5Hex", () => {
|
||||
});
|
||||
|
||||
describe("hashPassword (default driver: bcrypt)", () => {
|
||||
it("emits an argon2id hash and round-trips", async () => {
|
||||
it("emits a bcrypt hash and round-trips", async () => {
|
||||
mockEnv.PASSWORD_HASH = undefined;
|
||||
const h = await hashPassword("s3cret!");
|
||||
expect(h).toMatch(/^\$argon2id\$/);
|
||||
expect(h).toMatch(/^\$2y\$\d{2}\$/);
|
||||
expect(await verifyPassword("s3cret!", h)).toBe(true);
|
||||
expect(await verifyPassword("wrong", h)).toBe(false);
|
||||
});
|
||||
@@ -50,9 +51,25 @@ describe("hashPassword (PASSWORD_HASH=argon2id)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("argon2", () => {
|
||||
it("verifies an argon2 hash and round-trips", async () => {
|
||||
const h = await bcryptHash("hunter2");
|
||||
describe("verifyPassword", () => {
|
||||
it("verifies argon2id hashes", async () => {
|
||||
const h = await argon2id({
|
||||
password: "hunter2",
|
||||
salt: randomBytes(16),
|
||||
outputType: "encoded",
|
||||
parallelism: 1,
|
||||
iterations: 4,
|
||||
memorySize: 1024,
|
||||
hashLength: 32,
|
||||
});
|
||||
expect(await verifyPassword("hunter2", h)).toBe(true);
|
||||
expect(await verifyPassword("nope", h)).toBe(false);
|
||||
});
|
||||
|
||||
it("verifies legacy bcrypt hashes ($2y$)", async () => {
|
||||
mockEnv.PASSWORD_HASH = undefined;
|
||||
const h = await hashPassword("hunter2");
|
||||
expect(h).toMatch(/^\$2y\$/);
|
||||
expect(await verifyPassword("hunter2", h)).toBe(true);
|
||||
expect(await verifyPassword("nope", h)).toBe(false);
|
||||
});
|
||||
@@ -67,12 +84,12 @@ describe("isMd5Of", () => {
|
||||
});
|
||||
|
||||
describe("checkLogin", () => {
|
||||
it("upgrades a legacy md5 hash to the configured hash when conversion is enabled", async () => {
|
||||
it("upgrades a legacy md5 hash to bcrypt when conversion is enabled", async () => {
|
||||
mockEnv.PASSWORD_HASH = undefined;
|
||||
const stored = await md5Hex("oldpass");
|
||||
const res = await checkLogin("oldpass", stored, { convertPasswords: true });
|
||||
expect(res.valid).toBe(true);
|
||||
expect(res.upgradedHash).toMatch(/^\$argon2id\$/);
|
||||
expect(res.upgradedHash).toMatch(/^\$2y\$/);
|
||||
expect(await verifyPassword("oldpass", res.upgradedHash as string)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
+15
-10
@@ -1,9 +1,11 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { hash, verify } from "@node-rs/argon2";
|
||||
import { argon2id, argon2Verify, md5 } from "hash-wasm";
|
||||
|
||||
export const bcryptHash = (password: string) => hash(password);
|
||||
export const bcryptCompare = (password: string, hash: string) => verify(hash, password);
|
||||
import {
|
||||
argon2id,
|
||||
argon2Verify,
|
||||
bcrypt,
|
||||
bcryptVerify,
|
||||
md5,
|
||||
} from "hash-wasm";
|
||||
|
||||
import { env } from "@/env";
|
||||
|
||||
@@ -54,9 +56,14 @@ export async function hashPassword(password: string): Promise<string> {
|
||||
...argon2Params(),
|
||||
});
|
||||
}
|
||||
// native bcrypt emits $2a$/$2b$; normalise to the PHP-canonical $2y$ the
|
||||
// hash-wasm bcrypt emits $2a$; normalise to the PHP-canonical $2y$ the
|
||||
// emulator and existing AtomCMS rows use.
|
||||
const h = await bcryptHash(password);
|
||||
const h = await bcrypt({
|
||||
password,
|
||||
salt: randomBytes(16),
|
||||
costFactor: env.BCRYPT_ROUNDS,
|
||||
outputType: "encoded",
|
||||
});
|
||||
return h.replace(/^\$2[ab]\$/, "$2y$");
|
||||
}
|
||||
|
||||
@@ -89,9 +96,7 @@ export async function verifyPassword(
|
||||
}
|
||||
if (/^\$2[aby]\$/.test(stored)) {
|
||||
try {
|
||||
// PHP/AtomCMS store $2y$; native bcrypt only accepts $2a$/$2b$.
|
||||
const normalized = stored.replace(/^\$2y\$/, "$2a$");
|
||||
return await bcryptCompare(password, normalized);
|
||||
return await bcryptVerify({ password, hash: stored });
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user