Skip to content

fix(auth): prevent account enumeration on register and login (#83) - #90

Merged
Alimedhat000 merged 1 commit into
developfrom
fix/83-auth-enumeration
Aug 26, 2026
Merged

fix(auth): prevent account enumeration on register and login (#83)#90
Alimedhat000 merged 1 commit into
developfrom
fix/83-auth-enumeration

Conversation

@Alimedhat000

@Alimedhat000 Alimedhat000 commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Summary

Unauthenticated auth endpoints disclosed whether an email/username belongs to an existing account:

  • Register: 409 "Username or email already exists" vs 201 revealed collisions (and which field, via message wording).
  • Login: unknown-user and bad-password paths sent { error: result.error } where result.error is undefined on a successful parse — both bodies serialized to {}, leaking no message at all, and bcrypt was skipped for unknown users, creating a timing side-channel.

Changes

  • registerUser: single generic conflict body ({"error":"Registration failed"}) regardless of which field collided (409 kept, per issue direction).
  • loginUser: identical 401 {"error":"Invalid email or password"} for unknown-user and bad-password; also fixes the broken empty-body responses.
  • Timing parity: when the user doesn't exist, one bcrypt compare runs against a fixed dummy hash so response timing no longer reveals account existence.
  • Rate limiting on auth routes already exists (feat(server): rate-limit auth endpoints (re-land onto develop) #79), covering that part of the issue.
  • Collaborator add endpoint (owner-only) intentionally left as-is per Auth surfaces allow account enumeration (distinct errors for exists/not-found) #83's note to revisit after auth surfaces are uniform.

Testing

TDD: both new tests watched failing first, then green.

  • New: registration returns identical uniform 409 body for dup-email and dup-username, with a message that doesn't reveal fields/existence.
  • New: login responses for unknown user and bad password are byte-identical and carry a non-empty generic error.
  • Full server suite: 59 passed; lint + typecheck clean.

closes #83.

Registration now returns a single generic conflict message regardless of
which field collided, and login returns an identical 401 body with a
non-empty generic message for both unknown-user and bad-password cases
(previously the body serialized to {}). A dummy bcrypt compare equalizes
response timing when the user does not exist.
@Alimedhat000

Copy link
Copy Markdown
Owner Author

closes #83

@Alimedhat000
Alimedhat000 merged commit 39b13d6 into develop Aug 26, 2026
2 checks passed
@Alimedhat000
Alimedhat000 deleted the fix/83-auth-enumeration branch August 28, 2026 01:28
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.

Auth surfaces allow account enumeration (distinct errors for exists/not-found)

1 participant