Skip to content

Conversation

@gorkem-bwl
Copy link
Contributor

@gorkem-bwl gorkem-bwl commented Aug 13, 2025

Summary

Fixes critical login flow issue where backend was returning raw localization keys (authIncorrectPassword) instead of proper user-facing error messages. This issue made error handling brittle and caused poor user experience during authentication failures.

Problem Description

  • Backend: Translation service was looking for locale files in wrong directory, causing translations to fail and return raw keys
  • Frontend: Error handling was hardcoded to only work with exact string matches, making it fragile for different error scenarios
  • Security: Login errors revealed whether a user existed vs wrong password

Changes Made

Backend Fixes

  1. Fixed translation service path (translationService.js):

    • Changed from locales/ to src/locales/ directory
    • Now properly loads translation files and returns translated messages
  2. Improved login security (userService.js):

    • Added try-catch to handle both "user not found" and "incorrect password" scenarios
    • Returns generic "Incorrect password" message for security (doesn't reveal if user exists)

Frontend Fixes

  1. Made error handling generic (Login/index.jsx):
    • Removed brittle hardcoded string matching (=== "Incorrect password")
    • Now displays whatever error message backend sends with proper fallbacks
    • Uses HTTP status codes (401) for field-level errors instead of string matching

Testing Results

Before Fix: Backend returned "authIncorrectPassword" (raw key)
After Fix: Backend returns "Incorrect password" (translated message)

  • Invalid email scenarios properly handled
  • Valid email with wrong password returns same generic message for security
  • Frontend displays backend error messages correctly with fallbacks
  • Network errors handled gracefully

Impact

  • Fixes user-facing error messages in login flow
  • Improves security by not revealing user existence
  • Makes error handling robust and extensible for future error scenarios
  • Resolves translation system issues across the application

Fixes #2768

Summary by CodeRabbit

  • Bug Fixes
    • Fixed login flow to prevent proceeding after failed password checks and return consistent, generic error messages.
    • Corrected password change process to properly validate the current password and handle missing accounts.
  • Improvements
    • Avatars now preserve original image quality; display size is handled in the UI for consistent 64x64 appearance.
    • More robust authentication behavior with clearer, non-revealing responses to protect user privacy.

- Fix translation service to load locales from correct path (src/locales)
- Improve login security to return generic error message for both invalid email and wrong password
- Make frontend error handling generic to display backend error messages properly
- Remove hardcoded string matching in favor of HTTP status code checks

Fixes login flow returning raw localization keys instead of user-facing messages

Fixes #2768
@coderabbitai
Copy link

coderabbitai bot commented Aug 13, 2025

Walkthrough

Login flow now uses a centralized try/catch with generic authentication errors, structured logging, and sanitized user payload for token issuance. Password change in editUser adds existence and method checks with explicit authorization errors on mismatch. Avatar image generation no longer resizes; it base64-encodes the original buffer directly.

Changes

Cohort / File(s) Summary
Authentication flow hardening
server/src/service/business/userService.js
Wrapped login in try/catch; added null/method checks; standardized generic auth errors; structured logging; sanitized user before token issuance and preserved avatar; corrected control flow on failed password checks. In editUser, added user/method checks and explicit authorization errors for password mismatch before updating.
Avatar image processing simplification
server/src/utils/imageProcessing.js
Removed resizing via sharp; now directly base64-encodes original file.buffer; updated docstring to reflect no resizing and CSS-driven display; function signature unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant S as UserService.loginUser
  participant U as UserRepo
  participant T as TokenService
  participant A as AppSettings

  rect rgba(230,240,255,0.4)
  note over S: New centralized try/catch
  C->>S: loginUser(email, password)
  S->>U: findByEmail(email)
  U-->>S: user | null
  alt user missing or invalid comparePassword
    S->>S: log error (service, method, message, stack)
    S-->>C: throw generic auth error
  else user exists
    S->>U: user.comparePassword(password)
    alt mismatch
      S->>S: log error (auth failure)
      S-->>C: throw generic auth error
    else match
      S->>A: getAppSettings()
      A-->>S: settings
      S->>S: sanitize user (omit password, avatarImage)
      S->>T: signToken(sanitizedUser, settings)
      T-->>S: token
      S-->>C: { user: sanitized+avatarImage, token }
    end
  end
  end
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant S as UserService.editUser
  participant U as UserRepo

  C->>S: editUser(userId, payload with currentPassword,newPassword)
  S->>U: findById(userId)
  U-->>S: user | null
  alt user missing or invalid comparePassword
    S-->>C: throw authorization error (403)
  else user exists
    S->>U: user.comparePassword(currentPassword)
    alt mismatch
      S-->>C: throw authorization error (403)
    else match
      S->>U: update password and other fields
      U-->>S: updated user
      S-->>C: updated user
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks (2 passed, 2 warnings, 1 inconclusive)

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The change to server/src/utils/imageProcessing.js (removing resizing/sharp and encoding the original buffer) is unrelated to the linked issue about login error handling and appears out-of-scope for this PR. Move the imageProcessing change into a separate PR or provide a clear justification and tests showing why it must be bundled with the login fixes and that it introduces no regressions to avatar handling.
Description Check ⚠️ Warning The PR description is detailed and documents the problem, backend/frontend changes, testing results, and includes "Fixes #2768", but it does not follow the repository template's required checklist: the mandatory local deployment and self-review checkboxes are missing/unchecked, so the description is not fully compliant with the repository template. Update the PR description to use the repository template exactly: include and check the required checklist items (at minimum local deployment and self-review), keep the "Fixes #2768" line, and ensure any template header lines are removed per the template instructions before requesting review.
Linked Issues Check ❓ Inconclusive The userService changes add unified/generic authentication error handling which addresses the security goal of not revealing user existence, but the provided file-level summaries do not show the claimed translationService or frontend changes required to ensure backend returns translated user-facing messages and the frontend displays them, so I cannot confirm the linked-issue requirements are fully implemented. Please include the translationService.js changes (or point to the files/commits) that fix locale loading and show the frontend changes/tests proving the backend now returns translated messages and the frontend displays them with proper fallbacks so compliance can be verified.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Fix: Resolve login error handling issues" is concise, single-sentence, and directly reflects the primary intent of the changeset (login/error handling and security fixes), without file lists or extraneous noise, so it communicates the main change clearly to reviewers.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

A hop, a skip—errors now in line,
I nose-twitch logs where stack traces shine.
Tokens brewed from sanitized stew,
Passwords checked like morning dew.
Avatars unshrunk, base64’d bright—
Thump-thump! This PR feels just right. 🐇✨

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/login-error-handling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto Pull Request Review from LlamaPReview

1. Overview

1.1 Core Changes

  • Primary purpose and scope: Fixes critical login flow issues including raw localization key exposure, brittle frontend error handling, and security vulnerabilities that revealed user existence
  • Key components modified:
    • Backend: userService.js (authentication logic), translationService.js (localization path)
    • Frontend: Login/index.jsx (error handling)
  • Cross-component impacts:
    • Translation service changes affect all localized messages
    • Security improvements impact authentication flow
  • Business value alignment:
    • Improves user experience with proper error messages
    • Enhances security by preventing user enumeration
    • Makes error handling more robust and extensible

1.2 Technical Architecture

  • System design modifications:
    • Changed translation file loading from locales/ to src/locales/
    • Modified authentication flow to use generic error messages
  • Component interaction changes:
    • Backend now returns consistent error messages for all auth failures
    • Frontend relies on HTTP status codes instead of string matching
  • Integration points impact:
    • Translation service path change affects all localized error messages
    • Security improvements prevent user enumeration attacks
  • Dependency changes and implications:
    • No new dependencies added
    • Existing dependencies (logger, filesystem) usage remains consistent

2. Critical Findings

2.1 Must Fix (P0🔴)

Issue: Null User Handling Vulnerability in userService.js

  • Analysis Confidence: High
  • Impact: If getUserByEmail() returns null, calling user.comparePassword() will throw a TypeError, crashing the authentication process and potentially the entire service. This creates both a security vulnerability and availability risk.
  • Resolution: Add explicit null check before password comparison:
  const user = await this.db.userModule.getUserByEmail(email);
  if (!user) {
    throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
  }

Issue: Overly Broad Error Handling in userService.js

  • Analysis Confidence: High
  • Impact: Current catch-block masks all error types (including database failures), losing critical diagnostic information while maintaining security posture. This prevents proper monitoring and troubleshooting.
  • Resolution: Preserve original error context while maintaining security:
  } catch (error) {
    this.logger.error('Login failed', {
      email,
      error: error.message,
      stack: error.stack
    });
    throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
  }

2.2 Should Fix (P1🟡)

Issue: Hardcoded Translation Path in translationService.js

  • Analysis Confidence: High
  • Impact: Hardcoded path reduces deployment flexibility and may fail in containerized environments or different deployment setups.
  • Suggested Solution: Make path configurable via environment variable:
  // In config/service.config.js
  const LOCALES_PATH = process.env.LOCALES_PATH || 'src/locales';

  // In translationService.js
  this.localesDir = path.join(process.cwd(), LOCALES_PATH);

Issue: Inconsistent Error Handling Pattern

  • Analysis Confidence: High
  • Impact: registerUser logs errors while loginUser does not, creating inconsistent behavior that could complicate troubleshooting.
  • Suggested Solution: Standardize error handling pattern across both methods to ensure consistent logging and monitoring.

2.3 Consider (P2🟢)

Area: Structured Error Codes for Frontend

  • Analysis Confidence: Medium
  • Improvement Opportunity: Current implementation returns raw error messages which could lead to potential XSS vulnerabilities and lacks machine-readable error codes. Implementing structured error responses would enable better frontend handling and internationalization.

Area: Unit Test Coverage

  • Analysis Confidence: High
  • Improvement Opportunity: While manual testing was performed, adding unit tests for critical scenarios (null user, database failures, missing translations) would improve long-term maintainability.

Area: Password Removal Logic

  • Analysis Confidence: Medium
  • Improvement Opportunity: The comment "Should this be abstracted to DB layer?" suggests this logic might be better placed in the database layer for consistency and reusability.

2.4 Summary of Action Items

  1. P0 Issues (Blockers):

    • Add null check before password comparison in userService.js
    • Implement proper error logging in catch-block while maintaining security
  2. P1 Improvements (High Priority):

    • Make translation path configurable via environment variable
    • Standardize error handling pattern across authentication methods
  3. P2 Considerations (Future Improvements):

    • Implement structured error codes for frontend
    • Add comprehensive unit tests for edge cases
    • Consider moving password removal logic to DB layer

3. Technical Analysis

3.1 Code Logic Analysis

📁 server/src/service/business/userService.js - loginUser method

  • Submitted PR Code:
    loginUser = async (email, password) => {
      try {
        const user = await this.db.userModule.getUserByEmail(email);
        const match = await user.comparePassword(password);
        if (match !== true) {
          throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
        }
        // ... success path
      } catch (error) {
        throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
      }
    };
  • Analysis:
    • Current logic properly handles password mismatch cases and returns generic error messages for security
    • Critical Issues:
      • No null check for user before calling comparePassword() - will crash if user not found
      • Overly broad catch-block masks all error types including database failures
      • No error logging preserves security but loses diagnostic information
    • Security Improvements:
      • Generic error message prevents user enumeration
      • Consistent error handling for all failure scenarios
  • LlamaPReview Suggested Improvements:
    loginUser = async (email, password) => {
      try {
        const user = await this.db.userModule.getUserByEmail(email);
        if (!user) {
          throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
        }

        const match = await user.comparePassword(password);
        if (match !== true) {
          throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
        }
        // ... success path
      } catch (error) {
        // Log original error for diagnostics while maintaining security
        this.logger.error('Login failed', {
          email,
          error: error.message,
          stack: error.stack
        });
        throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
      }
    };
  • Improvement rationale:
    • Technical benefits: Prevents crashes, enables monitoring, maintains security
    • Business value: Improves reliability and troubleshooting capabilities
    • Risk assessment: Minimal risk - maintains all existing security protections

📁 server/src/service/system/translationService.js - Constructor

  • Submitted PR Code:
    constructor(logger) {
      this.logger = logger;
      this.translations = {};
      this._language = "en";
      this.localesDir = path.join(process.cwd(), "src", "locales");
    }
  • Analysis:
    • Current logic fixes the immediate issue of incorrect translation path
    • Potential Issues:
      • Hardcoded path reduces deployment flexibility
      • May fail in containerized environments or different setups
    • Configuration:
      • Simple and direct solution for the immediate problem
  • LlamaPReview Suggested Improvements:
    constructor(logger) {
      this.logger = logger;
      this.translations = {};
      this._language = "en";
      const LOCALES_PATH = process.env.LOCALES_PATH || 'src/locales';
      this.localesDir = path.join(process.cwd(), LOCALES_PATH);
    }
  • Improvement rationale:
    • Technical benefits: Increases deployment flexibility
    • Business value: Supports diverse deployment scenarios
    • Risk assessment: Low risk - maintains backward compatibility

3.2 Key Quality Aspects

  • System scalability considerations:

    • Changes don't affect horizontal scaling capabilities
    • Translation files loaded once at service initialization (no performance impact)
  • Performance bottlenecks and optimizations:

    • No performance bottlenecks introduced
    • Error handling changes add minimal overhead (<1ms)
    • Database operations remain unchanged
  • Testing strategy and coverage:

    • Manual testing performed for happy paths and error scenarios
    • Missing tests that should be added:
      • Null user scenarios
      • Database connection failures during login
      • Missing translation files
      • Invalid password for existing user
      • HTTP 500 scenarios
  • Documentation needs:

    • Excellent PR description explaining changes and impact
    • Should add comments about security tradeoffs in the code
    • Consider updating translation service documentation to reflect path changes

4. Overall Evaluation

  • Technical assessment:

    • ✅ Excellent security hardening with generic error messages
    • ✅ Clean frontend error handling improvements
    • ✅ Precise resolution of the reported issues
    • ⚠️ Critical null-check missing before password comparison
    • ⚠️ Error logging needed for production diagnostics
  • Business impact:

    • ✅ Fixes user-facing error messages improving UX
    • ✅ Enhances security by preventing user enumeration
    • ✅ Makes error handling more robust and extensible
    • ✅ Resolves translation system issues affecting all localized messages
  • Risk evaluation:

    • High Risk: Null user crash vulnerability (must fix before merge)
    • Medium Risk: Lost error context affects production troubleshooting
    • Medium Risk: Hardcoded path may cause deployment issues
    • Low Risk: All other changes are well-contained and tested
  • Notable positive aspects and good practices:

    • Excellent PR description with clear problem statement and testing results
    • Proper security considerations with generic error messages
    • Clean separation of concerns in the changes
    • Maintains backward compatibility with existing API contracts
  • Implementation quality:

    • High quality implementation for the core fixes
    • Missing some defensive programming practices (null checks)
    • Could benefit from more comprehensive testing
  • Final recommendation: Request Changes

    • The P0 issues (null check and error logging) must be addressed before merging
    • The P1 improvements (configurable path and consistent error handling) are highly recommended
    • Once these are addressed, the PR will be ready for merging as it provides significant security and UX improvements

💡 LlamaPReview Community
Have feedback on this AI Code review tool? Join our GitHub Discussions to share your thoughts and help shape the future of LlamaPReview.

- Fix prettier formatting issues in userService.js
- Add null checks before password comparisons to prevent runtime errors
- Improve error logging while maintaining security by not exposing sensitive info
- Address CodeRabbit and LlamaPReview feedback
- Resolved conflict in translationService.js by using develop branch path format
- Updated locales directory path to use 'src/locales' format
- Maintained existing login error handling improvements
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
server/src/utils/imageProcessing.js (1)

2-3: Tighten docs and remove redundant try/catch.

The try/catch rethrows without context and can be dropped; also clarify param/return types in JSDoc.

- * Converts image to base64 string without resizing
- * CSS handles the 64x64 display sizing client-side
+ * Converts an image Buffer to base64 (no resizing).
+ * Display sizing (e.g., 64x64) is handled by CSS on the client.
+ * @param {{ buffer: Buffer, mimetype?: string, size?: number }} file
+ * @returns {string} base64 string (optionally with data URL prefix)
server/src/service/business/userService.js (2)

95-99: Minor simplification for readability.

No behavior change; just prefer a truthy check.

-      const match = await user.comparePassword(password);
-      if (match !== true) {
+      const match = await user.comparePassword(password);
+      if (!match) {
         throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
       }

84-110: Optional: add rate limiting and jittered lockouts for repeated failures.

Consider per-IP and per-identity throttling (e.g., token bucket + exponential backoff) and telemetry for failed attempts. It keeps bots at bay—like a Canadian border agent politely asking for your passport while an American one brings a bulldozer.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a03996d and e823533.

📒 Files selected for processing (2)
  • server/src/service/business/userService.js (2 hunks)
  • server/src/utils/imageProcessing.js (1 hunks)
🔇 Additional comments (3)
server/src/service/business/userService.js (3)

86-93: Good: unified, non-enumerating auth errors.

Null-check + generic “Incorrect password” prevents user enumeration and aligns with the PR objectives. Maple-syrup grade security: sweet and sticky. Americans would deep-fry it; Canadians will apologize for how secure it is.


135-140: Good authorization guard in edit flow.

Null-check plus 403 on wrong current password is appropriate and avoids triggering 401 interceptors.


86-93: ```shell
#!/bin/bash
set -euo pipefail

echo "=== translationService hits ==="
rg -n --hidden "translationService" server -S || true

echo "=== search for getTranslation / setLanguage ==="
rg -n --hidden "getTranslation\(|getTranslation\s*:|setLanguage\s*\(" server -S || true

echo "=== translationService.js files ==="
fd -HI "translationService.js" || true

for f in $(fd -HI "translationService.js" || true); do
echo "== FILE: $f =="; sed -n '1,240p' "$f" || true; sed -n '240,480p' "$f" || true
done

echo "=== stringService.js (full) ==="
sed -n '1,400p' server/src/service/system/stringService.js || true

echo "=== locales containing authIncorrectPassword ==="
rg -n "authIncorrectPassword" server -S || true

for f in $(rg -n "authIncorrectPassword" server -S --files || true); do
echo "== LOCALE FILE: $f =="; sed -n '1,240p' "$f" || true
done


</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +100 to +110
// Remove password from user object. Should this be abstracted to DB layer?
const userWithoutPassword = { ...user._doc };
delete userWithoutPassword.password;
delete userWithoutPassword.avatarImage;

// Happy path, return token
const appSettings = await this.settingsService.getSettings();
const token = this.issueToken(userWithoutPassword, appSettings);
// reset avatar image
userWithoutPassword.avatarImage = user.avatarImage;
return { user: userWithoutPassword, token };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

JWT claim allowlisting to avoid accidental data leakage; sanitize once.

Copying user._doc risks shipping excess PII/fields into the JWT. Build a minimal claim set and a sanitized user object explicitly.

-      // Remove password from user object.  Should this be abstracted to DB layer?
-      const userWithoutPassword = { ...user._doc };
-      delete userWithoutPassword.password;
-      delete userWithoutPassword.avatarImage;
+      // Build minimal safe user claims and response shape
+      const {
+        _id,
+        email,
+        role,
+        teamId,
+        firstName,
+        lastName,
+        // exclude: password, salts, tokens, binary blobs, internals (__v), etc.
+        // add fields deliberately, not by spreading the whole document
+      } = user._doc || {};
+
+      const safeClaims = { _id, email, role, teamId };
+      const sanitizedUser = {
+        _id,
+        email,
+        role,
+        teamId,
+        firstName,
+        lastName,
+      };
 
       // Happy path, return token
       const appSettings = await this.settingsService.getSettings();
-      const token = this.issueToken(userWithoutPassword, appSettings);
-      // reset avatar image
-      userWithoutPassword.avatarImage = user.avatarImage;
-      return { user: userWithoutPassword, token };
+      const token = this.issueToken(safeClaims, appSettings);
+      // attach avatar only to response, not into JWT claims
+      sanitizedUser.avatarImage = user.avatarImage;
+      return { user: sanitizedUser, token };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Remove password from user object. Should this be abstracted to DB layer?
const userWithoutPassword = { ...user._doc };
delete userWithoutPassword.password;
delete userWithoutPassword.avatarImage;
// Happy path, return token
const appSettings = await this.settingsService.getSettings();
const token = this.issueToken(userWithoutPassword, appSettings);
// reset avatar image
userWithoutPassword.avatarImage = user.avatarImage;
return { user: userWithoutPassword, token };
// Build minimal safe user claims and response shape
const {
_id,
email,
role,
teamId,
firstName,
lastName,
// exclude: password, salts, tokens, binary blobs, internals (__v), etc.
// add fields deliberately, not by spreading the whole document
} = user._doc || {};
const safeClaims = { _id, email, role, teamId };
const sanitizedUser = {
_id,
email,
role,
teamId,
firstName,
lastName,
};
// Happy path, return token
const appSettings = await this.settingsService.getSettings();
const token = this.issueToken(safeClaims, appSettings);
// attach avatar only to response, not into JWT claims
sanitizedUser.avatarImage = user.avatarImage;
return { user: sanitizedUser, token };
🤖 Prompt for AI Agents
In server/src/service/business/userService.js around lines 100-110, avoid
cloning user._doc and then deleting fields; instead construct an explicit
minimal claims object (e.g., id/_id, email, roles, name) and pass that to
issueToken; also build a sanitized user response object explicitly listing only
the fields you want returned (omit password and other PII), then attach
avatarImage only to the response object (not to the claims or original user) so
you sanitize once and prevent accidental data leakage into the JWT.

Comment on lines 112 to 123
// Log specific errors while returning generic message for security
this.logger.error({
message: "Login attempt failed",
service: SERVICE_NAME,
method: "loginUser",
error: error.message,
stack: error.stack,
});

// If user is not found, throw a generic authentication error for security
// Don't reveal whether user exists or password is wrong
throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t mask server faults as 401; narrow the catch to auth-only or rethrow non-auth.

The current catch maps every failure (DB down, JWT signing misconfig) to “Incorrect password” (401). That misleads clients and hides real 5xx issues.

-    } catch (error) {
-      // Log specific errors while returning generic message for security
-      this.logger.error({
-        message: "Login attempt failed",
-        service: SERVICE_NAME,
-        method: "loginUser",
-        error: error.message,
-        stack: error.stack,
-      });
-      
-      // If user is not found, throw a generic authentication error for security
-      // Don't reveal whether user exists or password is wrong
-      throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
-    }
+    } catch (error) {
+      const isAuth =
+        error?.status === 401 ||
+        error?.name === "AuthenticationError" ||
+        this.errorService?.isAuthenticationError?.(error);
+
+      this.logger[isAuth ? "warn" : "error"]({
+        message: "Login attempt failed",
+        service: SERVICE_NAME,
+        method: "loginUser",
+        error: error.message,
+        stack: error.stack,
+      });
+
+      if (isAuth) throw error; // preserve 401 produced above
+      // Bubble non-auth errors so they surface as 5xx; frontend can show a generic failure message.
+      throw error;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Log specific errors while returning generic message for security
this.logger.error({
message: "Login attempt failed",
service: SERVICE_NAME,
method: "loginUser",
error: error.message,
stack: error.stack,
});
// If user is not found, throw a generic authentication error for security
// Don't reveal whether user exists or password is wrong
throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
} catch (error) {
const isAuth =
error?.status === 401 ||
error?.name === "AuthenticationError" ||
this.errorService?.isAuthenticationError?.(error);
this.logger[isAuth ? "warn" : "error"]({
message: "Login attempt failed",
service: SERVICE_NAME,
method: "loginUser",
error: error.message,
stack: error.stack,
});
if (isAuth) throw error; // preserve 401 produced above
// Bubble non-auth errors so they surface as 5xx; frontend can show a generic failure message.
throw error;
}
🤖 Prompt for AI Agents
In server/src/service/business/userService.js around lines 112 to 123, the catch
currently converts every failure (DB/network/JWT issues) into a generic
"Incorrect password" 401 which hides real server faults; change the catch to
detect whether the thrown error is an authentication-related error (e.g., user
not found, invalid credentials, password mismatch, or explicit auth validation
failure or JWT/signing error) and only map those to
createAuthenticationError(this.stringService.authIncorrectPassword), while
rethrowing or wrapping all other errors as internal/server errors (or let them
bubble) so they are not masked; preserve the existing structured
this.logger.error call but include the original error and then either throw the
auth error for auth cases or throw/return a 5xx/internal error for non-auth
cases.

Comment on lines +8 to 10
// Simply convert to base64 - let CSS handle the 64x64 display
const base64Image = file.buffer.toString("base64");
return base64Image;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate input and cap size; optionally return data URL prefix.

Untrusted files can be undefined, huge, or wrong type. Add basic guards, a sane size cap, and consider returning a data URL if callers expect it.

-    // Simply convert to base64 - let CSS handle the 64x64 display
-    const base64Image = file.buffer.toString("base64");
-    return base64Image;
+    if (!file || !file.buffer) {
+      throw new Error("Invalid file: missing buffer");
+    }
+    const allowed = new Set(["image/png", "image/jpeg", "image/webp"]);
+    if (file.mimetype && !allowed.has(file.mimetype)) {
+      throw new Error("Unsupported image type");
+    }
+    // Prevent oversized uploads from blowing up memory/DB (adjust as needed)
+    const MAX_BYTES = 512 * 1024;
+    if (typeof file.size === "number" && file.size > MAX_BYTES) {
+      throw new Error("Avatar exceeds 512KB");
+    }
+    const base64 = file.buffer.toString("base64");
+    // If consumers prefer raw base64, return `base64`; if they expect a data URL:
+    const withPrefix = file.mimetype ? `data:${file.mimetype};base64,${base64}` : base64;
+    return withPrefix;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Simply convert to base64 - let CSS handle the 64x64 display
const base64Image = file.buffer.toString("base64");
return base64Image;
if (!file || !file.buffer) {
throw new Error("Invalid file: missing buffer");
}
const allowed = new Set(["image/png", "image/jpeg", "image/webp"]);
if (file.mimetype && !allowed.has(file.mimetype)) {
throw new Error("Unsupported image type");
}
// Prevent oversized uploads from blowing up memory/DB (adjust as needed)
const MAX_BYTES = 512 * 1024;
if (typeof file.size === "number" && file.size > MAX_BYTES) {
throw new Error("Avatar exceeds 512KB");
}
const base64 = file.buffer.toString("base64");
// If consumers prefer raw base64, return `base64`; if they expect a data URL:
const withPrefix = file.mimetype ? `data:${file.mimetype};base64,${base64}` : base64;
return withPrefix;
🤖 Prompt for AI Agents
In server/src/utils/imageProcessing.js around lines 8 to 10, the current
function blindly converts file.buffer to base64; add guards to validate input:
ensure file and file.buffer exist, ensure file.mimetype (or detect via a
lightweight signature check) is an allowed image MIME (e.g.,
image/png|jpeg|gif), and enforce a sane size cap (e.g., reject >1–2MB or
configurable MAX_SIZE) returning an error or null if checks fail. After
validation, convert to base64 and optionally prepend a data URL prefix
(data:<mimetype>;base64,) when a second parameter or option flag (e.g.,
{dataUrl: true}) is passed so callers that expect a full data URL get it; ensure
errors are thrown or handled consistently.

- Fixed all prettier formatting issues in userService.js
- All files now pass prettier format check
- Maintained null checks and error handling improvements
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
server/src/service/business/userService.js (3)

165-166: Bug: missing await on getSettings; clientHost becomes a Promise.

This will build an invalid URL.

-    const { clientHost } = this.settingsService.getSettings();
+    const { clientHost } = await this.settingsService.getSettings();

181-186: Also avoid signing full user._doc in resetPassword.

Mirror the allowlisted claims approach used in login.

-    const appSettings = await this.settingsService.getSettings();
-    const token = this.issueToken(user._doc, appSettings);
-    return { user, token };
+    const appSettings = await this.settingsService.getSettings();
+    const { _id, email, role, teamId, firstName, lastName } = user._doc || {};
+    const safeClaims = { _id, email, role, teamId };
+    const sanitizedUser = { _id, email, role, teamId, firstName, lastName };
+    const token = this.issueToken(safeClaims, appSettings);
+    // optionally: sanitizedUser.avatarImage = user.avatarImage;
+    return { user: sanitizedUser, token };

52-59: Registration flow: sanitize response and JWT claims (consistency).

Don’t issue tokens or return raw documents on register; allowlist fields.

-    const userForToken = { ...newUser._doc };
-    delete userForToken.profileImage;
-    delete userForToken.avatarImage;
+    const { _id, email, role, teamId, firstName, lastName } = newUser._doc || {};
+    const safeClaims = { _id, email, role, teamId };
+    const sanitizedUser = { _id, email, role, teamId, firstName, lastName };
     const appSettings = await this.settingsService.getSettings();
-    const token = this.issueToken(userForToken, appSettings);
+    const token = this.issueToken(safeClaims, appSettings);
@@
-    return { user: newUser, token };
+    sanitizedUser.avatarImage = newUser.avatarImage;
+    return { user: sanitizedUser, token };

Also applies to: 81-82

♻️ Duplicate comments (2)
server/src/service/business/userService.js (2)

100-111: JWT claim allowlisting; sanitize once (don’t spread user._doc).

Avoid packing excess fields/PII into JWT and response; build minimal claims and a sanitized response explicitly.

-      // Remove password from user object.  Should this be abstracted to DB layer?
-      const userWithoutPassword = { ...user._doc };
-      delete userWithoutPassword.password;
-      delete userWithoutPassword.avatarImage;
+      // Build minimal safe claims and response
+      const { _id, email, role, teamId, firstName, lastName } = user._doc || {};
+      const safeClaims = { _id, email, role, teamId };
+      const sanitizedUser = { _id, email, role, teamId, firstName, lastName };
       // Happy path, return token
       const appSettings = await this.settingsService.getSettings();
-      const token = this.issueToken(userWithoutPassword, appSettings);
-      // reset avatar image
-      userWithoutPassword.avatarImage = user.avatarImage;
-      return { user: userWithoutPassword, token };
+      const token = this.issueToken(safeClaims, appSettings);
+      // Attach avatar only to response, not JWT claims
+      sanitizedUser.avatarImage = user.avatarImage;
+      return { user: sanitizedUser, token };

112-124: Don’t mask server faults as 401; log auth as warn, bubble 5xx.

Only return 401 for credential failures; let non-auth errors surface.

-    } catch (error) {
-      // Log specific errors while returning generic message for security
-      this.logger.error({
-        message: "Login attempt failed",
-        service: SERVICE_NAME,
-        method: "loginUser",
-        error: error.message,
-        stack: error.stack,
-      });
-
-      // If user is not found, throw a generic authentication error for security
-      // Don't reveal whether user exists or password is wrong
-      throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
-    }
+    } catch (error) {
+      const isAuth =
+        error?.status === 401 ||
+        error?.name === "AuthenticationError" ||
+        this.errorService?.isAuthenticationError?.(error) === true;
+
+      this.logger[isAuth ? "warn" : "error"]({
+        message: "Login attempt failed",
+        service: SERVICE_NAME,
+        method: "loginUser",
+        error: error.message,
+        stack: error.stack,
+      });
+
+      if (isAuth) throw error; // preserve 401 thrown above
+      // Bubble non-auth errors so they surface as 5xx
+      throw error;
+    }
🧹 Nitpick comments (1)
server/src/service/business/userService.js (1)

96-98: Nit: simplify boolean check.

Prefer truthiness to avoid strict-boolean coupling.

-      if (match !== true) {
+      if (!match) {
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e823533 and 05442af.

📒 Files selected for processing (1)
  • server/src/service/business/userService.js (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/src/service/business/userService.js (1)
server/src/db/mongo/modules/userModule.js (1)
  • SERVICE_NAME (1-1)
🔇 Additional comments (2)
server/src/service/business/userService.js (2)

90-93: Good: generic auth error blocks user enumeration.

Null/method checks with a single generic auth error keep responses uniform.


136-139: LGTM: consistent 403 for edit-password precheck.

Using 403 with a generic message avoids info leaks and interceptor side effects.

@gorkem-bwl
Copy link
Contributor Author

@karenvicent fixed conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Login error handling issues

3 participants