From 4ee3a09b2fc8039dae8cd029d5379cd144be1708 Mon Sep 17 00:00:00 2001 From: Bereket Engida Date: Fri, 29 Nov 2024 21:44:20 +0300 Subject: [PATCH] fix: use object parameters for password verification to clarify hash and password roles --- packages/better-auth/src/api/routes/sign-in.ts | 6 +++--- .../better-auth/src/api/routes/update-user.ts | 8 ++++---- .../better-auth/src/crypto/password.test.ts | 18 ++++++++++++------ packages/better-auth/src/crypto/password.ts | 5 ++++- packages/better-auth/src/init.ts | 2 +- .../src/plugins/phone-number/index.ts | 6 +++--- .../better-auth/src/plugins/username/index.ts | 8 ++++---- packages/better-auth/src/types/options.ts | 2 +- packages/better-auth/src/utils/password.ts | 18 +++++++++--------- 9 files changed, 41 insertions(+), 32 deletions(-) diff --git a/packages/better-auth/src/api/routes/sign-in.ts b/packages/better-auth/src/api/routes/sign-in.ts index a2c6daaa15..0bdd003d96 100644 --- a/packages/better-auth/src/api/routes/sign-in.ts +++ b/packages/better-auth/src/api/routes/sign-in.ts @@ -370,10 +370,10 @@ export const signInEmail = createAuthEndpoint( message: "Unexpected error", }); } - const validPassword = await ctx.context.password.verify( - currentPassword, + const validPassword = await ctx.context.password.verify({ + hash: currentPassword, password, - ); + }); if (!validPassword) { ctx.context.logger.error("Invalid password"); throw new APIError("UNAUTHORIZED", { diff --git a/packages/better-auth/src/api/routes/update-user.ts b/packages/better-auth/src/api/routes/update-user.ts index 8b12480f8b..cacad862f0 100644 --- a/packages/better-auth/src/api/routes/update-user.ts +++ b/packages/better-auth/src/api/routes/update-user.ts @@ -191,10 +191,10 @@ export const changePassword = createAuthEndpoint( }); } const passwordHash = await ctx.context.password.hash(newPassword); - const verify = await ctx.context.password.verify( - account.password, - currentPassword, - ); + const verify = await ctx.context.password.verify({ + hash: account.password, + password: currentPassword, + }); if (!verify) { throw new APIError("BAD_REQUEST", { message: "Incorrect password", diff --git a/packages/better-auth/src/crypto/password.test.ts b/packages/better-auth/src/crypto/password.test.ts index 65edd8e202..56e57b74c6 100644 --- a/packages/better-auth/src/crypto/password.test.ts +++ b/packages/better-auth/src/crypto/password.test.ts @@ -12,7 +12,7 @@ describe("Password hashing and verification", () => { it("should verify a correct password", async () => { const password = "correctPassword123!"; const hash = await hashPassword(password); - const isValid = await verifyPassword(hash, password); + const isValid = await verifyPassword({ hash, password }); expect(isValid).toBe(true); }); @@ -20,7 +20,7 @@ describe("Password hashing and verification", () => { const correctPassword = "correctPassword123!"; const incorrectPassword = "wrongPassword456!"; const hash = await hashPassword(correctPassword); - const isValid = await verifyPassword(hash, incorrectPassword); + const isValid = await verifyPassword({ hash, password: incorrectPassword }); expect(isValid).toBe(false); }); @@ -34,15 +34,21 @@ describe("Password hashing and verification", () => { it("should handle long passwords", async () => { const password = "a".repeat(1000); const hash = await hashPassword(password); - const isValid = await verifyPassword(hash, password); + const isValid = await verifyPassword({ hash, password }); expect(isValid).toBe(true); }); it("should be case-sensitive", async () => { const password = "CaseSensitivePassword123!"; const hash = await hashPassword(password); - const isValidLower = await verifyPassword(hash, password.toLowerCase()); - const isValidUpper = await verifyPassword(hash, password.toUpperCase()); + const isValidLower = await verifyPassword({ + hash, + password: password.toLowerCase(), + }); + const isValidUpper = await verifyPassword({ + hash, + password: password.toUpperCase(), + }); expect(isValidLower).toBe(false); expect(isValidUpper).toBe(false); }); @@ -50,7 +56,7 @@ describe("Password hashing and verification", () => { it("should handle Unicode characters", async () => { const password = "пароль123!"; const hash = await hashPassword(password); - const isValid = await verifyPassword(hash, password); + const isValid = await verifyPassword({ hash, password }); expect(isValid).toBe(true); }); }); diff --git a/packages/better-auth/src/crypto/password.ts b/packages/better-auth/src/crypto/password.ts index 244568ab8d..5551a8b883 100644 --- a/packages/better-auth/src/crypto/password.ts +++ b/packages/better-auth/src/crypto/password.ts @@ -26,7 +26,10 @@ export const hashPassword = async (password: string) => { return `${salt}:${encodeHex(key)}`; }; -export const verifyPassword = async (hash: string, password: string) => { +export const verifyPassword = async ({ + hash, + password, +}: { hash: string; password: string }) => { const [salt, key] = hash.split(":"); const targetKey = await generateKey(password, salt!); return constantTimeEqual(targetKey, decodeHex(key!)); diff --git a/packages/better-auth/src/init.ts b/packages/better-auth/src/init.ts index 51fb94d278..cbc810151f 100644 --- a/packages/better-auth/src/init.ts +++ b/packages/better-auth/src/init.ts @@ -171,7 +171,7 @@ export type AuthContext = { secondaryStorage: SecondaryStorage | undefined; password: { hash: (password: string) => Promise; - verify: (password: string, hash: string) => Promise; + verify: (data: { password: string; hash: string }) => Promise; config: { minPasswordLength: number; maxPasswordLength: number; diff --git a/packages/better-auth/src/plugins/phone-number/index.ts b/packages/better-auth/src/plugins/phone-number/index.ts index b4f1820333..1603c4aacf 100644 --- a/packages/better-auth/src/plugins/phone-number/index.ts +++ b/packages/better-auth/src/plugins/phone-number/index.ts @@ -199,10 +199,10 @@ export const phoneNumber = (options?: { message: "Unexpected error", }); } - const validPassword = await ctx.context.password.verify( - currentPassword, + const validPassword = await ctx.context.password.verify({ + hash: currentPassword, password, - ); + }); if (!validPassword) { ctx.context.logger.error("Invalid password"); throw new APIError("UNAUTHORIZED", { diff --git a/packages/better-auth/src/plugins/username/index.ts b/packages/better-auth/src/plugins/username/index.ts index 0f1d6d2990..d8669b3ecb 100644 --- a/packages/better-auth/src/plugins/username/index.ts +++ b/packages/better-auth/src/plugins/username/index.ts @@ -107,10 +107,10 @@ export const username = () => { message: "Unexpected error", }); } - const validPassword = await ctx.context.password.verify( - currentPassword, - ctx.body.password, - ); + const validPassword = await ctx.context.password.verify({ + hash: currentPassword, + password: ctx.body.password, + }); if (!validPassword) { ctx.context.logger.error("Invalid password"); throw new APIError("UNAUTHORIZED", { diff --git a/packages/better-auth/src/types/options.ts b/packages/better-auth/src/types/options.ts index e863313cf2..7a12e14fe6 100644 --- a/packages/better-auth/src/types/options.ts +++ b/packages/better-auth/src/types/options.ts @@ -206,7 +206,7 @@ export interface BetterAuthOptions { */ password?: { hash?: (password: string) => Promise; - verify?: (hash: string, password: string) => Promise; + verify?: (data: { hash: string; password: string }) => Promise; }; /** * Automatically sign in the user after sign up diff --git a/packages/better-auth/src/utils/password.ts b/packages/better-auth/src/utils/password.ts index ba8c2906e7..b308153845 100644 --- a/packages/better-auth/src/utils/password.ts +++ b/packages/better-auth/src/utils/password.ts @@ -16,10 +16,10 @@ export async function validatePassword( if (!credentialAccount || !currentPassword) { return false; } - const compare = await ctx.context.password.verify( - currentPassword, - data.password, - ); + const compare = await ctx.context.password.verify({ + hash: currentPassword, + password: data.password, + }); return compare; } @@ -29,15 +29,15 @@ export async function checkPassword(userId: string, c: GenericEndpointContext) { (account) => account.providerId === "credential", ); const currentPassword = credentialAccount?.password; - if (!credentialAccount || !currentPassword) { + if (!credentialAccount || !currentPassword || !c.body.password) { throw new APIError("BAD_REQUEST", { message: "No password credential found", }); } - const compare = await c.context.password.verify( - currentPassword, - c.body.password, - ); + const compare = await c.context.password.verify({ + hash: currentPassword, + password: c.body.password, + }); if (!compare) { throw new APIError("BAD_REQUEST", { message: "Invalid password",