fix(security): rate-limit /auth/jwt/login and /auth/register (#644)#677
Conversation
The SlowAPI Limiter is built without default/application limits, so it only throttles routes carrying an explicit @limiter.limit decorator. The fastapi-users auth routes are mounted via library factories and were never decorated, leaving login and registration open to unthrottled credential brute-force. Add enforce_auth_rate_limit, a FastAPI dependency that performs the auth-tier check at request time, and attach it to the /auth/jwt and /auth/register router mounts (mirroring the allow_registration dependency pattern). Buckets are namespaced per-endpoint and per-client. Auth uses a dedicated key that fails closed for undeterminable IPs (stable shared bucket) instead of the per-request unique bucket get_rate_limit_key assigns, so brute-force cannot bypass the limit by stripping its source IP. Closes #644
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds auth-tier rate limiting for the fastapi-users-mounted ChangesAuth Endpoint Rate Limiting
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Code Review — fix(security): rate-limit /auth/jwt/login and /auth/register (#644)Overall: Approve with minor notes. This is a well-scoped, correctly reasoned security fix. The core logic is sound, the fail-closed behavior for unknown IPs is the right call, and the four tests cover the important cases. Notes below are improvements rather than blockers. What the PR doesThe SlowAPI Security — correct
Code quality — minor issues1. Private API surface ( This accesses the inner # SlowAPI's public @limiter.limit API can only decorate functions we own.
# For library-mounted routes we must call the inner limits backend directly.
if not limiter.limiter.hit(limit.limit, limit.scope, key):2.
3. Docstring mismatch in The Returns block says Tests — good coverage, one clarification worth adding
One edge case worth clarifying: Known limitations (documented — acceptable)
Checklist
|
…egister test intent (#644)
Follow-up ReviewAll three notes from my previous review have been addressed in the docs commit:
Approve. Security fix is correct, fail-closed behavior is verified, and the code is now self-documenting. Ready to merge. |
Summary
Closes #644 (P0 beta blocker, security).
The SlowAPI
Limiteris constructed withoutdefault_limits/application_limits, so it only throttles routes that carry an explicit@limiter.limit(...)decorator. The fastapi-users auth routes (/auth/jwt/login,/auth/register) are mounted via library factory functions and were never decorated, so the existingrate_limit_auth()(10/min) never applied to them — leaving login and registration open to unthrottled credential brute-force.Fix
codeframe/lib/rate_limiter.py— newenforce_auth_rate_limit, a FastAPI dependency that runs the auth-tier rate-limit check at request time (the@limiter.limitdecorator can't be used here because it binds at import time and must wrap an endpoint we own). Buckets are namespaced per-endpoint path + per-client, so login and register throttle independently.get_auth_rate_limit_key— an auth-specific key that fails closed for undeterminable client IPs (one stableip:unknownbucket) instead of the per-request-unique bucketget_rate_limit_keyassigns for general routes. This prevents bypassing the limit by stripping the source IP. General-route behavior is unchanged.codeframe/auth/router.py— attach the dependency to the/auth/jwtand/auth/registermounts (mirroring the existingallow_registrationdependency). On the register mount it runs first so throttling precedes the bootstrap 403 check.RateLimitExceededhandler → 429 + headers + audit log.Acceptance criteria
/auth/jwt/loginreturns 429 after exceeding the auth-tier limit./auth/registeris likewise limited.Tests
tests/ui/test_auth_rate_limit.py(4 tests,@pytest.mark.v2):test_login_is_rate_limited— login returns 429 once the limit is exceeded.test_register_is_rate_limited— register likewise.test_unknown_client_fails_closed— undeterminable-IP clients share a stable bucket and are still throttled.test_no_throttle_when_disabled— dependency is a strict no-op when rate limiting is off.Verification: full
tests/ui/suite (398) + the new tests pass;ruffclean. Cross-family review (codex) raised one Major (auth fail-open for unknown IPs) which is fixed here.Known limitations
auth/router.py; when enabled they should receive the same dependency (out of scope here).Summary by CodeRabbit
New Features
Tests