Skip to content

feat(api): Add per-IP rate limiting middleware to /v1/auth/login and /v1/auth/signup #105

Description

@Emmyt24

Description

crates/api/src/lib.rs::build_router applies a permissive CORS layer but no rate limiting whatsoever, anywhere. crates/api/src/auth.rs::login already takes care to return an identical error for 'no such user' and 'wrong password' to prevent account enumeration, but with zero throttling, an attacker can still attempt unlimited password guesses per second against any known email address. This ticket adds basic per-IP middleware-level rate limiting as a first line of defense; a more complete persistent lockout policy is tracked separately as one of the hard/complex issues.

Requirements and Context

  • Add tower_governor (or tower-http's own rate-limit primitives if the team prefers staying within the already-depended-on tower-http, but tower_governor is the more common choice for per-IP limiting on axum/tower) as a new dependency scoped to crates/api.
  • Apply a per-IP limit (propose something like 10 requests/minute, but justify your chosen figures in the PR description) specifically to /v1/auth/login and /v1/auth/signup, not the whole API.
  • Ensure the limiter correctly identifies the client IP behind a proxy consistent with the existing crate::audit::client_ip header-parsing convention (X-Forwarded-For then X-Real-IP) rather than defaulting to the immediate TCP peer address, which would be the load balancer's IP in production.

Suggested Execution

Branch: feat/api/auth-rate-limiting-middleware

Implement Changes

  • Add the rate-limiting layer in crates/api/src/lib.rs::build_router, scoped only to the auth routes (e.g. via a nested Router with its own .layer(...), merged into the main router).

Test and Commit

  • login_is_rate_limited_after_the_configured_threshold_per_ip.
  • signup_is_rate_limited_after_the_configured_threshold_per_ip.
  • rate_limit_is_scoped_per_ip_not_global (two distinct simulated client IPs each get their own budget).
  • unrelated_routes_are_not_rate_limited_by_the_auth_layer.
  • Run cargo test -p octo-api locally before committing.

Example Commit Message

feat(api): add per-IP rate limiting to the login and signup routes

Neither login nor signup had any request throttling, allowing unlimited
password-guessing attempts per second against any known email address
despite the enumeration-safe error message. Adds per-IP rate limiting scoped
to just these two routes as a first line of defense.

Guidelines

  • Scope the limiter narrowly to the auth routes — do not rate-limit the whole API in this PR, since wallet/withdrawal/sponsor routes have their own distinct throttling needs better addressed by the separate persistent-lockout hard issue.
  • Make sure the client-IP extraction matches the existing X-Forwarded-For/X-Real-IP convention already used in crates/api/src/audit.rs::client_ip, so behavior is consistent across the codebase.
  • Reference this issue with Closes #<issue-number> in the PR description.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26area/backendBackend crates: api, store, ingest, webhooks, bin/serverdifficulty/mediumMedium difficulty

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions