using System.Security.Cryptography; using System.Text.Json.Nodes; using Azaion.Missions.JwksMock.Services; namespace Azaion.Missions.JwksMock.Endpoints; public static class JwksEndpoint { /// /// GET /.well-known/jwks.json. Mirrors the shape the production /// admin issuer publishes — JsonWebKey 'kty=EC, crv=P-256, alg=ES256, /// use=sig' with base64url x/y coordinates. /// public static IResult Handle(KeyStore keys) { var keysArray = new JsonArray(); foreach (var key in keys.PublishedKeys()) { var p = key.Ec.ExportParameters(includePrivateParameters: false); keysArray.Add(new JsonObject { ["kty"] = "EC", ["use"] = "sig", ["alg"] = "ES256", ["crv"] = "P-256", ["kid"] = key.Kid, ["x"] = Base64Url.Encode(p.Q.X!), ["y"] = Base64Url.Encode(p.Q.Y!) }); } var doc = new JsonObject { ["keys"] = keysArray }; return Results.Json(doc, statusCode: 200, contentType: "application/json") .WithCacheControl("public, max-age=60"); } private static IResult WithCacheControl(this IResult result, string value) => new CacheControlResult(result, value); private sealed class CacheControlResult(IResult inner, string cacheControl) : IResult { public Task ExecuteAsync(HttpContext httpContext) { httpContext.Response.Headers.CacheControl = cacheControl; return inner.ExecuteAsync(httpContext); } } }