47 lines
1.7 KiB
C#
47 lines
1.7 KiB
C#
using Server.WatchBody.Core.Dal.Src.Domain;
|
||
using Server.WatchBody.Core.Dal.Src.Repository;
|
||
using Server.WatchBody.Web.Services;
|
||
|
||
namespace Server.WatchBody.Web.Middleware;
|
||
|
||
/// <summary>
|
||
/// Аутентификация X-Api-Key → athlete_id (spec 02-api-key-middleware).
|
||
/// /health — без ключа. 401 единым телом: нет ключа / чужой / заблокирован.
|
||
/// </summary>
|
||
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);
|
||
}
|
||
}
|