using Server.WatchBody.Core.Dal.Src.Domain; using Server.WatchBody.Core.Dal.Src.Repository; using Server.WatchBody.Web.Services; namespace Server.WatchBody.Web.Middleware; /// /// Аутентификация X-Api-Key → athlete_id (spec 02-api-key-middleware). /// /health — без ключа. 401 единым телом: нет ключа / чужой / заблокирован. /// public class ApiKeyMiddleware(RequestDelegate next) { public const string AthleteIdItem = "athlete_id"; public async Task InvokeAsync(HttpContext context, IAthleteRepository athletes) { // /health без ключа — только GET (З-42 п. 8): прочие методы идут // в аутентификацию и получают 401, а не деградацию 404/405 if (context.Request.Path == "/health" && HttpMethods.IsGet(context.Request.Method)) { await next(context); return; } var key = context.Request.Headers["X-Api-Key"].FirstOrDefault(); Athlete? athlete = null; if (!string.IsNullOrEmpty(key)) { var hash = ApiKeyHash.Of(key); athlete = athletes.Select(new AthleteFilter { ApiKeyHash = hash }) .FirstOrDefault(); } if (athlete == null || athlete.IsBlocked) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; await context.Response.WriteAsync("""{"error":"unauthorized"}"""); return; } context.Items[AthleteIdItem] = athlete.Id; await next(context); } }