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); } } }