20d997bc5a
## Automated fix for [bug 5360](https://sonarly.com/issue/5360?type=bug) **Severity:** `critical` ### Summary 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. 1. 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. 2. 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. 3. 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. 4. 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 (commit2abb6adb61). However, the signInUp flow's validatePassword() method was introduced later by Antoine Moreaux on 2025-03-17 (commitbda835b9f8) without replicating this same defensive check. 5. 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 [`bda835b`](https://github.com/twentyhq/twenty/commit/bda835b9f82) ### Suggested 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 - **Code:** [packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts:229 - if (userData.type === 'existingUser') {](https://github.com/twentyhq/twenty/blob/main/packages/twenty-server/src/engine/core-modules/auth/services/auth.service.ts#L229) --- *Generated by [Sonarly](https://sonarly.com)* Co-authored-by: Sonarly Claude Code <claude-code@sonarly.com>