using System.Net; using Microsoft.Extensions.Primitives; namespace Alchegos.HCI.Filters; public class WebhookAuthFilter : IEndpointFilter { private readonly ILogger _logger; public WebhookAuthFilter(ILogger logger) { _logger = logger; } public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var expectedToken = Environment.GetEnvironmentVariable("HCI_WEBHOOK_AUTH_TOKEN"); var allowedOrigin = Environment.GetEnvironmentVariable("HCI_WEBHOOK_AUTH_ALLOW_ORIGIN"); if (string.IsNullOrEmpty(expectedToken) || string.IsNullOrEmpty(allowedOrigin)) { _logger.LogCritical("Required environment variables 'HCI_WEBHOOK_AUTH_TOKEN' or 'HCI_WEBHOOK_AUTH_TOKEN' are not set."); _logger.LogInformation($"token: {expectedToken}, allowedOrigin: {allowedOrigin}"); return Results.StatusCode((int)HttpStatusCode.InternalServerError); } var httpContext = context.HttpContext; if (!httpContext.Request.Headers.TryGetValue("X-Api-Key", out StringValues receivedTokenValues) || receivedTokenValues.Count == 0) { _logger.LogWarning("Webhook request rejected: Missing 'X-Api-Key' header."); return Results.Unauthorized(); } var receivedToken = receivedTokenValues.ToString(); if (!SecureCompare(receivedToken, expectedToken)) { _logger.LogWarning("Webhook request rejected: Invalid API Token provided."); return Results.Unauthorized(); } if (!httpContext.Request.Headers.TryGetValue("Origin", out StringValues originValues) || originValues.Count == 0) { _logger.LogWarning("Webhook request rejected: Missing 'Origin' header."); return Results.Forbid(); } var origin = originValues.ToString(); if (!origin.Equals(allowedOrigin, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning("Webhook request rejected: Origin '{ReceivedOrigin}' is not allowed. Expected '{AllowedOrigin}'.", origin, allowedOrigin); return Results.Forbid(); } _logger.LogInformation("Webhook request from Origin '{Origin}' authenticated successfully.", origin); return await next(context); } private static bool SecureCompare(string a, string b) { a ??= string.Empty; b ??= string.Empty; if (a.Length != b.Length) return false; int diff = 0; for (int i = 0; i < a.Length; i++) diff |= a[i] ^ b[i]; return diff == 0; } }