Skip to content

feat: rate limit password and email change mutations (JEF-43) - #156

Merged
mankatcheung merged 2 commits into
mainfrom
feat/rate-limit-password-email-change
Jul 30, 2026
Merged

mankatcheung merged 2 commits into
mainfrom
feat/rate-limit-password-email-change

Conversation

@mankatcheung

@mankatcheung mankatcheung commented Jul 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

Add sliding-window rate limiting to UPDATE_PASSWORD and REQUEST_EMAIL_CHANGE mutations to prevent brute-force attacks.

Changes

  • Added UPDATE_PASSWORD and REQUEST_EMAIL_CHANGE rate limit constants in constants.ts
  • Added updatePasswordRateLimiter and requestEmailChangeRateLimiter to DI container
  • Updated UpdatePasswordUseCase with rate limiting by user ID
  • Updated RequestEmailChangeUseCase with rate limiting by user ID
  • Updated all test files to include rate limiter mocks

Test Plan

  • Unit tests pass
  • Typecheck passes
  • Authorization guard tests updated

Closes JEF-43

Summary by CodeRabbit

  • New Features
    • Added rate limiting for password update attempts.
    • Added rate limiting for email change requests.
    • Password updates are limited to 5 attempts per 15 minutes.
    • Email change requests are limited to 3 attempts per hour.
  • Bug Fixes
    • Blocked requests now stop before account or email processing when limits are exceeded.
    • Rate-limited actions return a clear rate-limit error.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mankatcheung, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1df82eae-17e3-4244-8358-d7362b127219

📥 Commits

Reviewing files that changed from the base of the PR and between 251a90e and 9fcce0c.

📒 Files selected for processing (7)
  • apps/api/src/__tests__/application/user/RequestEmailChangeUseCase.test.ts
  • apps/api/src/__tests__/application/user/UpdatePasswordUseCase.test.ts
  • apps/api/src/__tests__/security/authorizationGuards.test.ts
  • apps/api/src/constants.ts
  • apps/api/src/http/container.ts
  • apps/api/src/use-cases/user/RequestEmailChangeUseCase.ts
  • apps/api/src/use-cases/user/UpdatePasswordUseCase.ts

Walkthrough

Authentication rate limiting now covers password updates and email-change requests. Dedicated limiter configurations are registered in the container, enforced before downstream use-case work, and covered by updated tests.

Changes

Authentication rate limiting

Layer / File(s) Summary
Rate-limit configuration and container wiring
apps/api/src/constants.ts, apps/api/src/http/container.ts
Adds password-update and email-change limits, exposes both limiters through Cradle, and registers singleton RateLimiter instances.
Use-case rate-limit enforcement
apps/api/src/use-cases/user/RequestEmailChangeUseCase.ts, apps/api/src/use-cases/user/UpdatePasswordUseCase.ts
Consumes per-user limiters before validation or repository access and throws RATE_LIMITED when attempts are denied.
Rate-limit dependency test coverage
apps/api/src/__tests__/application/user/*, apps/api/src/__tests__/security/authorizationGuards.test.ts
Updates dependency setup and verifies denied attempts avoid downstream repository operations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant UpdatePasswordUseCase
  participant IRateLimiter
  participant userRepository
  Client->>UpdatePasswordUseCase: execute(userId)
  UpdatePasswordUseCase->>IRateLimiter: consume(update-password:user:userId)
  IRateLimiter-->>UpdatePasswordUseCase: allowed or false
  UpdatePasswordUseCase->>userRepository: findById(userId)
Loading

Possibly related PRs

Poem

A bunny guards the password gate,
And email requests must patiently wait.
Two little clocks begin to chime,
Blocking excess attempts in time.
RATE_LIMITED hops into place! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding rate limiting to password and email change mutations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rate-limit-password-email-change

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.

@github-actions

github-actions Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/http/container.ts`:
- Around line 361-372: Replace the production registrations for
updatePasswordRateLimiter and requestEmailChangeRateLimiter with the shared
atomic Redis-backed sliding-window limiter, using the existing production
limiter configuration and dependency wiring. Keep the current process-local
RateLimiter available only under the established local-development/testing path,
and preserve each limiter’s existing RATE_LIMIT thresholds and windows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f6afe325-861b-4888-b58d-6c8d378e13b3

📥 Commits

Reviewing files that changed from the base of the PR and between 72e10bf and 251a90e.

📒 Files selected for processing (7)
  • apps/api/src/__tests__/application/user/RequestEmailChangeUseCase.test.ts
  • apps/api/src/__tests__/application/user/UpdatePasswordUseCase.test.ts
  • apps/api/src/__tests__/security/authorizationGuards.test.ts
  • apps/api/src/constants.ts
  • apps/api/src/http/container.ts
  • apps/api/src/use-cases/user/RequestEmailChangeUseCase.ts
  • apps/api/src/use-cases/user/UpdatePasswordUseCase.ts

Comment on lines +361 to +372
updatePasswordRateLimiter: asValue(
new RateLimiter(
RATE_LIMIT.UPDATE_PASSWORD.MAX_ATTEMPTS,
RATE_LIMIT.UPDATE_PASSWORD.WINDOW_MS,
),
),
requestEmailChangeRateLimiter: asValue(
new RateLimiter(
RATE_LIMIT.REQUEST_EMAIL_CHANGE.MAX_ATTEMPTS,
RATE_LIMIT.REQUEST_EMAIL_CHANGE.WINDOW_MS,
),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Use a shared sliding-window backend for these production limiters.

The supplied RateLimiter is a process-local Map with a { count, resetAt } bucket, so these registrations are neither Redis-backed nor sliding-window. In a multi-instance deployment, attempts can be spread across instances (or cleared by restart), weakening both new brute-force protections. Register a shared, atomic production limiter and retain the in-memory implementation only for local development/testing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/http/container.ts` around lines 361 - 372, Replace the
production registrations for updatePasswordRateLimiter and
requestEmailChangeRateLimiter with the shared atomic Redis-backed sliding-window
limiter, using the existing production limiter configuration and dependency
wiring. Keep the current process-local RateLimiter available only under the
established local-development/testing path, and preserve each limiter’s existing
RATE_LIMIT thresholds and windows.

Add sliding-window rate limiting to UPDATE_PASSWORD and REQUEST_EMAIL_CHANGE
mutations to prevent brute-force attacks. Uses fixed-window counters with
Redis-backed storage in production.
…ate-limit keys

Moves assertValidPassword above the updatePassword rate-limit check so that

invalid passwords fail fast without consuming the rate-limit budget. Adds

assertions for the exact keys passed to the rate limiter in both

UpdatePasswordUseCase and RequestEmailChangeUseCase tests.
@mankatcheung
mankatcheung force-pushed the feat/rate-limit-password-email-change branch from 251a90e to 9fcce0c Compare July 30, 2026 22:04
@mankatcheung
mankatcheung merged commit f3b5cb1 into main Jul 30, 2026
11 checks passed
@mankatcheung
mankatcheung deleted the feat/rate-limit-password-email-change branch August 1, 2026 15:39
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.

1 participant