using System.Net.Http.Json; using System.Text.Json; namespace Azaion.Missions.E2E.Helpers; /// /// Invokes the missions service's test-only POST /test/refresh-jwks /// endpoint, which forces the JWKS /// to re-fetch immediately. The endpoint is mapped only when /// ASPNETCORE_ENVIRONMENT=Test; production deployments never expose it. /// /// /// Why this exists: Microsoft.IdentityModel.Tokens hard-pins the /// MinimumAutomaticRefreshInterval floor to 5 minutes via a static /// field. JWKS-rotation e2e scenarios (NFT-SEC-11, NFT-RES-07) cannot rely on /// the proactive refresh path inside the 15-minute CI window. The signature- /// failure refresh path the JwtBearer middleware exposes /// (RefreshOnIssuerKeyNotFound) is bypassed because the service uses a /// custom IssuerSigningKeyResolver. Hence: explicit refresh via this /// hook, no test poisons later tests. /// public static class JwksRefreshHelper { public static async Task ForceRefreshAsync(HttpClient missions, CancellationToken cancel = default) { ArgumentNullException.ThrowIfNull(missions); using var resp = await missions.PostAsync("/test/refresh-jwks", content: null, cancel) .ConfigureAwait(false); resp.EnsureSuccessStatusCode(); var body = await resp.Content.ReadFromJsonAsync(cancel).ConfigureAwait(false); var kids = body.GetProperty("kids"); var result = new string[kids.GetArrayLength()]; for (var i = 0; i < result.Length; i++) result[i] = kids[i].GetString() ?? ""; return result; } }