diff --git a/Marechai.Server/Controllers/AuthController.cs b/Marechai.Server/Controllers/AuthController.cs index a1501925..76070a8f 100644 --- a/Marechai.Server/Controllers/AuthController.cs +++ b/Marechai.Server/Controllers/AuthController.cs @@ -43,6 +43,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RateLimiting; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Configuration; @@ -52,6 +53,7 @@ namespace Marechai.Server.Controllers; [ApiController] [Route("auth")] +[EnableRateLimiting("AuthEndpoints")] public class AuthController (UserManager userManager, MarechaiContext context, TokenService tokenService, IConfiguration configuration, diff --git a/Marechai.Server/Program.cs b/Marechai.Server/Program.cs index ee5585d5..a5c49a8e 100644 --- a/Marechai.Server/Program.cs +++ b/Marechai.Server/Program.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.IO.Compression; using System.Linq; +using System.Threading.RateLimiting; using System.Text.Json; using System.Text.Json.Serialization; using Aaru.CommonTypes.Interop; @@ -354,6 +355,24 @@ file class Program builder.Services.AddMarechaiEmail(builder.Configuration); + builder.Services.AddRateLimiter(options => + { + options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; + + // Strict per-IP limit for all auth endpoints (login, register, 2FA, password reset, etc.) + options.AddPolicy("AuthEndpoints", httpContext => + RateLimitPartition.GetSlidingWindowLimiter( + httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown", + _ => new SlidingWindowRateLimiterOptions + { + Window = TimeSpan.FromMinutes(15), + SegmentsPerWindow = 3, + PermitLimit = 20, + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 0 + })); + }); + builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton();