55 lines
2.0 KiB
C#
55 lines
2.0 KiB
C#
using System.Text.Json;
|
|
using General_Ledger_Service.DTOs.Common;
|
|
|
|
namespace General_Ledger_Service.Middleware
|
|
{
|
|
public class ApiKeyMiddleware
|
|
{
|
|
private const string ApiKeyHeaderName = "X-Api-Key";
|
|
// /dev-tools/generate-api-key is the bootstrap tool that produces the very first key -
|
|
// it can never require a key to reach it, or there'd be no way to generate one. It's
|
|
// still gated to Development-only inside DevToolsService itself; excluding it here only
|
|
// means "no key needed," not "reachable in every environment."
|
|
private static readonly string[] ExcludedPathPrefixes = { "/health", "/swagger", "/api/v1/dev-tools" };
|
|
|
|
private readonly RequestDelegate _next;
|
|
|
|
public ApiKeyMiddleware(RequestDelegate next)
|
|
{
|
|
_next = next;
|
|
}
|
|
|
|
public async Task InvokeAsync(HttpContext context, IConfiguration configuration)
|
|
{
|
|
var path = context.Request.Path.Value ?? string.Empty;
|
|
if (ExcludedPathPrefixes.Any(prefix => path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
await _next(context);
|
|
return;
|
|
}
|
|
|
|
var expectedApiKey = configuration["ApiKey:Value"];
|
|
var providedApiKey = context.Request.Headers[ApiKeyHeaderName].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(expectedApiKey) || providedApiKey != expectedApiKey)
|
|
{
|
|
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
|
context.Response.ContentType = "application/json";
|
|
|
|
var response = new ApiResponse
|
|
{
|
|
StatusCode = StatusCodes.Status401Unauthorized,
|
|
Success = false,
|
|
Message = "Invalid or missing API key",
|
|
Data = null
|
|
};
|
|
|
|
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
|
|
return;
|
|
}
|
|
|
|
await _next(context);
|
|
}
|
|
}
|
|
}
|