Microsoft.AspNetCore.RateLimiting lives in the Web block. The kit ships an auth policy that throttles authentication-flow endpoints by IP — login, refresh, forgot-password, reset-password, confirm-email, self-register. The point is brute-force protection on the endpoints that matter, without imposing a global rate limit on legitimate API traffic.
Endpoints in the auth policy
The Identity module wires these endpoints to the auth policy:
| Endpoint | Why |
|---|---|
POST /api/v1/identity/tokens/generate | Login attempts |
POST /api/v1/identity/tokens/refresh | Token refresh attempts |
POST /api/v1/identity/users/forgot-password | Password reset request abuse |
POST /api/v1/identity/users/reset-password | Password reset attempts |
POST /api/v1/identity/users/confirm-email | Email confirmation attempts |
POST /api/v1/identity/users/self-register | Self-registration abuse |
endpoints.MapPost("/tokens/generate", handler) .RequireRateLimiting("auth");AuthRateLimitWiringTests (in Identity.Tests) verifies every endpoint that should have the policy actually does. Run the tests after touching the Identity module’s endpoint registrations.
How it’s configured
The policy lives in RateLimitOptions in appsettings:
{ "RateLimitOptions": { "AuthPolicy": { "PermitLimit": 10, "Window": "00:01:00", "QueueLimit": 0, "QueueProcessingOrder": "OldestFirst" } }}That’s a fixed-window limiter — 10 requests per minute per IP, with no queueing (excess requests get 429 immediately). Tune to your traffic patterns:
- Lower numbers for high-risk endpoints (forgot-password, confirm-email) — abuse is more harmful here than legitimate retries.
- Higher numbers for high-volume endpoints (token refresh on a popular product) — block too low and your real users get 429s during burst traffic.
Adding additional policies
Register additional policies in RateLimitOptions and apply them with .RequireRateLimiting("policy") on the relevant endpoints:
{ "RateLimitOptions": { "PublicApi": { "PermitLimit": 100, "Window": "00:01:00" } }}endpoints.MapGet("/api/v1/public/products", handler) .RequireRateLimiting("PublicApi");For per-tenant rate limits, write a custom partition key resolver that uses HttpContext.User claims:
options.AddPolicy("PerTenant", context =>{ var tenantId = context.User.FindFirst("tenant")?.Value ?? "anonymous"; return RateLimitPartition.GetFixedWindowLimiter(tenantId, _ => new FixedWindowRateLimiterOptions { PermitLimit = 1000, Window = TimeSpan.FromMinutes(1) });});What rate limiting doesn’t protect against
- Distributed brute force — 10,000 IPs sending one request each per minute each gets through every per-IP limit. Combine rate limiting with credential-stuffing detection, MFA, and account lockout (which the kit ships via
IdentityOptions). - Slow-rate attacks — an attacker sending 1 request per second from one IP wouldn’t hit a 10/min limit but might still be malicious. Combine with anomaly detection on auth-failure rates.
- Application-layer DDoS — rate limiting helps but isn’t a complete defence. Use a CDN / WAF in front (Cloudflare, AWS WAF, Azure Front Door) for layer-7 protection.
Watching for false positives
When a legitimate user hits the rate limit, they see HTTP 429 + Retry-After. In production, log every 429 with the source IP and endpoint:
- A single IP hitting 429 repeatedly on login → likely brute force.
- Many IPs hitting 429 on the same legitimate endpoint → your limit is too low.
- One IP hitting 429 on refresh → the user’s client may be misconfigured (no token caching, retrying too aggressively).
Set up an alert on “429 rate > X% of requests” so you catch tuning issues early.
Related
- Security overview — broader auth + abuse protection.
- Quota building block — per-tenant resource budgets (different concern).
- HTTP resilience — caller-side back-off when receiving 429.