using System.Net.Http.Json; using System.Text.Json.Serialization; namespace Azaion.Missions.E2E; /// /// Wraps POST {jwks-mock}/sign. Token signing happens ONLY inside the /// jwks-mock container — the consumer never imports a JWT signing library. /// public sealed class TokenMinter : IDisposable { private readonly HttpClient _http; private readonly Uri _signUrl; public TokenMinter(string signUrl) { _signUrl = new Uri(signUrl); // The jwks-mock CA is added to the container OS trust bundle by // docker-entrypoint.sh; an HttpClient with default handler picks it up // through OpenSSL. _http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; } public Task MintDefaultAsync(CancellationToken ct = default) => MintAsync(new SignRequest(Permissions: "FL"), ct); public async Task MintAsync(SignRequest request, CancellationToken ct = default) { using var response = await _http.PostAsJsonAsync(_signUrl, request, ct).ConfigureAwait(false); response.EnsureSuccessStatusCode(); var body = await response.Content .ReadFromJsonAsync(cancellationToken: ct) .ConfigureAwait(false); if (body is null) throw new InvalidOperationException("jwks-mock /sign returned an empty body"); return new MintedToken(body.Token, body.Kid); } public void Dispose() => _http.Dispose(); } public sealed record SignRequest( [property: JsonPropertyName("iss")] string? Iss = null, [property: JsonPropertyName("aud")] string? Aud = null, [property: JsonPropertyName("sub")] string? Sub = null, [property: JsonPropertyName("exp_offset_seconds")] int? ExpOffsetSeconds = null, [property: JsonPropertyName("permissions")] string? Permissions = null, [property: JsonPropertyName("alg_override")] string? AlgOverride = null, [property: JsonPropertyName("kid_override")] string? KidOverride = null); internal sealed record SignResponse( [property: JsonPropertyName("token")] string Token, [property: JsonPropertyName("kid")] string Kid); public sealed record MintedToken(string Jwt, string Kid) { public string AsBearer() => $"Bearer {Jwt}"; }