Fix: Password validation requires data and hash arguments#18189
Merged
FelixMalfait merged 1 commit intomainfrom Feb 24, 2026
Merged
Conversation
https://sonarly.com/issue/5360?type=bug When an existing user created via OAuth (with no password hash) attempts to sign in through the signInUp password flow, their null passwordHash is passed directly to bcrypt.compare, which throws an unhandled 'data and hash arguments required' error instead of a user-friendly message. Fix: Added a null check on userData.existingUser.passwordHash inside the validatePassword private method of AuthService, immediately before calling signInUpService.validatePassword. When passwordHash is null or undefined (i.e. the user was created via OAuth and never set a password), an AuthException with code INVALID_INPUT is thrown with the user-friendly message 'User was not created with email/password'. This mirrors the identical guard that already exists in validateLoginWithPassword at line 177, preventing bcrypt.compare from receiving a null argument and crashing with an unhandled error.
Contributor
Greptile SummaryAdds a defensive null check for
Confidence Score: 5/5
Important Files Changed
Last reviewed commit: 9540337 |
|
Hey @FelixMalfait! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
Contributor
LogDetails |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated fix for bug 5360
Severity:
criticalSummary
When an existing user created via OAuth (with no password hash) attempts to sign in through the signInUp password flow, their null passwordHash is passed directly to bcrypt.compare, which throws an unhandled 'data and hash arguments required' error instead of a user-friendly message.
User Impact
Users who originally registered via Google or Microsoft OAuth and then attempt to sign in using email/password through the signInUp flow cannot authenticate. They see a server error instead of a helpful message like 'User was not created with email/password'. This blocks the user from accessing the CRM entirely.
Root Cause
Proximate cause: bcrypt.compare() at auth.util.ts:19 throws 'data and hash arguments required' because the passwordHash argument is null.
Why did bcrypt throw? Because compareHash() was called with a null passwordHash value. The compareHash function at auth.util.ts:19 passes its arguments directly to bcrypt.compare without any null check.
Why was passwordHash null? Because SignInUpService.validatePassword() at sign-in-up.service.ts:154 received a null passwordHash from AuthService.validatePassword() at auth.service.ts:232. The value userData.existingUser.passwordHash is null for users who were originally created via an OAuth provider (Google, Microsoft) and never set a password.
Why was there no null check before calling bcrypt? Because AuthService.validatePassword() at line 229-233 does not check whether userData.existingUser.passwordHash is null before passing it to signInUpService.validatePassword(). The TypeScript type annotation says passwordHash is string, but the UserEntity column is declared nullable: true (user.entity.ts:73), so the runtime value can be null despite the type system.
Why does this code path lack the null check when another path has it? The validateLoginWithPassword() method at auth.service.ts:177 correctly checks if (!user.passwordHash) and throws a user-friendly AuthException 'Incorrect login method'. This guard was added by Thomas Trompette on 2024-08-07 (commit 2abb6ad). However, the signInUp flow's validatePassword() method was introduced later by Antoine Moreaux on 2025-03-17 (commit bda835b) without replicating this same defensive check.
Why was this not caught? The signInUp validatePassword method was written assuming the existingUser would always have a passwordHash when the auth provider is Password. This assumption is incorrect because the signInUp flow can match an existing OAuth-created user (who has no passwordHash) when someone tries to sign up with email/password using an email already registered via OAuth. No test covers this cross-provider scenario in the signInUp path.
Root cause: The AuthService.validatePassword() method (auth.service.ts:229-233) in the signInUp code path is missing a null guard on userData.existingUser.passwordHash before passing it to bcrypt.compare. The fix should check for null/undefined passwordHash and throw a descriptive AuthException, mirroring the existing guard in validateLoginWithPassword at line 177.
Introduced by: Antoine Moreaux on 2025-03-17 in commit
bda835bSuggested Fix
Added a null check on userData.existingUser.passwordHash inside the validatePassword private method of AuthService, immediately before calling signInUpService.validatePassword. When passwordHash is null or undefined (i.e. the user was created via OAuth and never set a password), an AuthException with code INVALID_INPUT is thrown with the user-friendly message 'User was not created with email/password'. This mirrors the identical guard that already exists in validateLoginWithPassword at line 177, preventing bcrypt.compare from receiving a null argument and crashing with an unhandled error.
Evidence
Generated by Sonarly