Skip to content

Security: rate limiting on anonymous auth endpoints (login/refresh) #143

Closed

Description

@mforce

This was generated by AI during triage.

Context

From a security audit (2026-07-22, OWASP-checklist review). The anonymous auth endpoints have no throttling of any kind, and the configured Identity account lockout is separately known to be inert (#128) — so today there is zero brute-force defence on login. An attacker can spray passwords across accounts (rotating emails sidesteps per-user lockout even once #128 is fixed) or hammer the refresh endpoint.

Agent Brief

Category: enhancement
Summary: Add server-side rate limiting to the anonymous auth endpoints (login and refresh).

Current behavior:
POST /api/v1/auth/login and POST /api/v1/auth/refresh allow anonymous access with no rate limiting. Unlimited attempts per client, no throttling, no 429 responses anywhere in the API.

Desired behavior:

  • A strict rate-limit policy applies to the auth endpoint group (e.g. on the order of 10 requests per 15-minute window per client — exact numbers are the implementer's call, but they should stop password spraying while never bothering a legitimate farm user who typoes a password a few times).
  • Exceeding the limit returns 429 with a Retry-After header and a ProblemDetails body consistent with the API's other error responses.
  • The partition key is the client IP. Behind the reverse proxy this must be the original client IP (forwarded-headers handling — see the security-headers/forwarded-headers issue; if that hasn't landed yet, this issue must handle X-Forwarded-For from the trusted proxy itself or the limiter will throttle the proxy's IP, i.e. everyone at once).
  • Non-auth endpoints are unaffected.

Key interfaces:

  • ASP.NET Core's built-in rate limiter (AddRateLimiter + UseRateLimiter), with the policy attached to the auth endpoint group (RequireRateLimiting or group-level metadata) rather than individual endpoints, so future auth endpoints inherit it.
  • The limiter's rejection handler — must produce ProblemDetails, matching the API's existing error contract.

Acceptance criteria:

  • Integration test: bursting the login endpoint past the limit returns 429 with Retry-After; requests within the limit behave as before.
  • Integration test: same for the refresh endpoint.
  • Integration test: a non-auth endpoint is not rate limited by this policy.
  • 429 body is ProblemDetails-shaped.
  • Limits are configurable via app configuration with safe defaults baked in.

Out of scope:

Activity

  1. added
    enhancementNew feature or request
    sliceThin vertical work item
    area:apiAPI/endpoint layer
    on Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions