Skip to content

sec(auth): unify login error response for unknown vs wrong-password (closes #416)#887

Open
cristim wants to merge 1 commit into
feat/multicloud-web-frontendfrom
fix/416-wave21
Open

sec(auth): unify login error response for unknown vs wrong-password (closes #416)#887
cristim wants to merge 1 commit into
feat/multicloud-web-frontendfrom
fix/416-wave21

Conversation

@cristim
Copy link
Copy Markdown
Member

@cristim cristim commented May 30, 2026

Summary

  • Collapses two distinct login failure messages into the single generic response "invalid email or password" for all authentication failure paths, eliminating the account-enumeration oracle reported in sec(auth): login returns distinct error messages for non-existent vs existing accounts (account enumeration) #416
  • Adds a dummy bcrypt.CompareHashAndPassword call in Login when the user-lookup fails so a missing-account request takes the same wall time as a wrong-password request on a real account, closing the timing-side-channel oracle
  • Extends the existing TestLogin_OWASPEnumerationInvariant test from 5 to 6 scenarios by adding the real Postgres store-error path ((nil, err) not (nil, nil)) and adds TestHandler_login_ErrorEquivalence at the handler layer

Coordination with #388

PR #886 (closes #388) fixes MFA-enrollment leakage at the handler layer (service errors mapped to machine-readable codes after the password check passes). This PR fixes the pre-password-check path (distinct messages for missing vs existing users). The two changes are non-overlapping in terms of code paths and diff regions; whichever lands first, the other rebases cleanly. If both go out in the same merge window, rebase this one on top of #886.

Files changed

  • internal/auth/service.go -- unified error messages in getUserAndValidateStatus and verifyPasswordAndMFA; dummy bcrypt call in Login
  • internal/auth/service_password.go -- added dummyPasswordHash sentinel var (pre-computed bcrypt-12 hash)
  • internal/auth/service_test.go -- updated 5 existing assertions
  • internal/auth/service_lockout_test.go -- updated 2 assertions, added store-error scenario to OWASP invariant test
  • internal/api/handler_auth_test.go -- added TestHandler_login_ErrorEquivalence

Test plan

  • All 1854 existing tests pass (go test ./internal/auth/... ./internal/api/...)
  • TestLogin_OWASPEnumerationInvariant now has 6 scenarios and covers both (nil, nil) and (nil, err) store return shapes
  • TestHandler_login_ErrorEquivalence positively asserts identical HTTP status (401) and body (invalid email or password) for both the unknown-user and wrong-password paths
  • No "authentication failed" or "Check your email address and password" strings remain in any login-flow error path

…loses #416)

Collapse two distinct login failure messages into a single generic response
"invalid email or password" for all authentication failure paths:

- GetUserByEmail returns error (real store path for missing row): was
  "authentication failed", now "invalid email or password"
- user == nil, inactive user, locked user, empty password hash,
  wrong password: all now return "invalid email or password"

Add a dummy bcrypt.CompareHashAndPassword call in Login when
getUserAndValidateStatus fails so the timing of a missing-account
request matches a wrong-password request on a real account, closing
the timing-side-channel oracle alongside the message oracle.

Coordination with #388: #388 fixes MFA-enrollment leakage at the handler
layer (service errors mapped to machine-readable codes). That PR has no
open worktree/PR and addresses a different path (post-password-check MFA
signals). This PR fixes the pre-password-check path. The two changes are
non-overlapping; whichever lands first, the other rebases cleanly.

Tests: extend TestLogin_OWASPEnumerationInvariant from 5 to 6 scenarios
by adding the real Postgres store error path ((nil, err) not (nil, nil));
add TestHandler_login_ErrorEquivalence which positively asserts that both
paths produce identical HTTP status and body at the handler layer.
@cristim cristim added triaged Item has been triaged priority/p2 Backlog-worthy urgency/this-quarter Within the quarter impact/many Affects most users effort/m Days type/security Security finding labels May 30, 2026
@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 30, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes and 40 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8e6cb5e-8402-4dc5-8ec1-5a07837afc2f

📥 Commits

Reviewing files that changed from the base of the PR and between 4956d66 and 9eaafcb.

📒 Files selected for processing (5)
  • internal/api/handler_auth_test.go
  • internal/auth/service.go
  • internal/auth/service_lockout_test.go
  • internal/auth/service_password.go
  • internal/auth/service_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/416-wave21

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

effort/m Days impact/many Affects most users priority/p2 Backlog-worthy triaged Item has been triaged type/security Security finding urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant