From c04877464a7a49357497dc5f2b2cf5120dddef08 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 15 Jul 2025 14:38:02 -0700 Subject: [PATCH] Add RateLimiting For API --- .../Controllers/AuthenticationController.cs | 3 ++- src/Server/Program.cs | 27 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs index 5eaf19e..9b37afd 100755 --- a/src/Server/Controllers/AuthenticationController.cs +++ b/src/Server/Controllers/AuthenticationController.cs @@ -34,7 +34,8 @@ namespace BoredCareers.Controllers { await _databaseService.SetAccount(test); List claims = new List() { - new Claim("ID", test.ID.ToString()) + new Claim("ID", test.ID.ToString()), + new Claim(ClaimTypes.NameIdentifier, test.ID.ToString()) }; await HttpContext.SignInAsync( diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 15d312b..1df3be9 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -2,7 +2,10 @@ using Microsoft.AspNetCore.Authentication.Cookies; using BoredCareers.Controllers.Payment; using BoredCareers.Services; using BoredCareers.Services.DatabaseService; +using System.Threading.RateLimiting; +using Microsoft.AspNetCore.RateLimiting; using Stripe; +using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); @@ -90,9 +93,27 @@ builder.Services.AddAuthentication( options => { options.SlidingExpiration = true; }); -builder.Services.AddCors( o => o.AddDefaultPolicy( builder => { +builder.Services.AddCors(o => o.AddDefaultPolicy(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); // No CORS -} ) ); +})); + +builder.Services.AddRateLimiter(options => { + options.AddPolicy("PerUserPolicy", httpContext => { + // Identify the user (assumes authenticated user with NameIdentifier claim) + var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? httpContext.User.Identity?.Name + ?? httpContext.Connection.RemoteIpAddress?.ToString(); + + return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions { + TokenLimit = 10, // max 10 requests + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 0, + ReplenishmentPeriod = TimeSpan.FromSeconds(15), + TokensPerPeriod = 2, + AutoReplenishment = true + }); + }); +}); // Pages Service builder.Services.AddControllers(); @@ -113,7 +134,7 @@ app.UseCors(); app.UseRouting(); app.UseAuthentication(); -app.MapControllers(); +app.MapControllers().RequireRateLimiting("perUserPolicy"); app.MapFallbackToFile("index.html");