feat(auth): rate-limit /forgot-password per (IP, email) (PMS-680) #459
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/PMS-680-forgot-password-rate-limit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Rate-limits
POST /api/v1/auth/forgot-passwordper (source IP, lowercased email), closing the PMS-680 gap where the endpoint was unthrottled and a known address could be reset-email bombed.How
LoginLimiter::new()(hardcoded 20/5) becomesAuthRateLimiter::new(ip_per_min, email_per_min). Each unauthenticated auth endpoint constructs its own instance with its own quotas and buckets, matching the portal's separate-limiter pattern (one endpoint's traffic never consumes another's quota).rate_limitedJSON +Retry-After+no-store) into a sharedrate_limited_responsehelper used by bothloginandforgot_password.forgot_passwordnow takesConnectInfo<SocketAddr>and checks the limiter inline (the email is in the request body, the same reason login checks inline). Over quota returns 429; under quota the silent-success / no-enumeration behaviour is unchanged.Tests
tests/auth.rs::forgot_password_rate_limit_triggers_429: the 4th request for one email within a minute returns 429 withRetry-Afterand therate_limitedbody, mirroringlogin_rate_limit_triggers_429. Fullcargo test --test authpasses against Postgres;cargo check --all-targetsand clippy are clean.Notes
The 10/3 quotas are the values proposed in the issue; tune if load testing suggests. The limiter is in-memory (same as the existing login limiter), so it is single-process only; horizontal scale needs a shared store, the same caveat login already carries.
#PMS-680