Revised Registration
All checks were successful
Registration / build-deploy (push) Successful in 9m8s

This commit is contained in:
Grae Jones
2026-03-22 09:37:28 -07:00
parent 8de463cd17
commit fae2226581
6 changed files with 389 additions and 215 deletions

View File

@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Registration.Auth;
/// <summary>
/// Validates the x-functions-key header on admin endpoints.
///
/// In Azure Functions mode, admin endpoints used AuthorizationLevel.Function —
/// the Functions host validated the key automatically. In ASP.NET Core mode
/// that host doesn't exist, so this filter replicates the same behaviour.
///
/// The same header name (x-functions-key) and the same env var
/// (Registration__FunctionKey) are used in both modes, so the Management API
/// and Gateway require zero changes when the host is swapped.
///
/// Key configuration (docker-compose .env already has this):
/// Registration__FunctionKey=mra0B2boC5m36E7CUn-Urhwp7k3t3QvPZKjJvtNVEdVgAzFuuaAyRA==
///
/// ── SWAP note ─────────────────────────────────────────────────────────────
/// This file is only compiled and used in ASP.NET Core mode.
/// When restoring Azure Functions mode, the [ApiKeyAuth] attributes on admin
/// endpoints in RegistrationFunctions.cs are replaced by AuthorizationLevel.Function
/// in the [HttpTrigger] attributes, and this filter becomes unused (but harmless).
/// ─────────────────────────────────────────────────────────────────────────
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class ApiKeyAuthAttribute : Attribute, IResourceFilter
{
private const string HeaderName = "x-functions-key";
public void OnResourceExecuting(ResourceExecutingContext context)
{
var config = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
var expected = config["Registration:FunctionKey"];
// If no key is configured, block all admin traffic — fail secure.
if (string.IsNullOrWhiteSpace(expected))
{
context.Result = new ObjectResult(new { ok = false, error = "Admin API key not configured on server" })
{
StatusCode = StatusCodes.Status503ServiceUnavailable
};
return;
}
if (!context.HttpContext.Request.Headers.TryGetValue(HeaderName, out var provided)
|| !string.Equals(expected, provided, StringComparison.Ordinal))
{
context.Result = new UnauthorizedObjectResult(new { ok = false, error = "Invalid or missing API key" });
}
}
public void OnResourceExecuted(ResourceExecutedContext context) { }
}