Skip to content

Commit 3d0de01

Browse files
authored
Add jwks endpoint (#27)
1 parent 7af15c2 commit 3d0de01

File tree

6 files changed

+40
-12
lines changed

6 files changed

+40
-12
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Jose;
2+
using System.Security.Cryptography;
3+
4+
public static class JwkHelper
5+
{
6+
public static WebApplication MapJwk(this WebApplication app, out Jwk jwk)
7+
{
8+
var ecSigner = ECDsa.Create(ECCurve.NamedCurves.nistP521); // Typically, you'd load this from a secure location and not create a new one each time
9+
10+
jwk = new Jwk(ecSigner, false)
11+
{
12+
KeyId = "bank-api-2025-1"
13+
};
14+
15+
var keySet = new JwkSet(jwk);
16+
17+
app.MapGet("/.well-known/jwks.json", () => TypedResults.Ok(keySet.ToDictionary()))
18+
.ExcludeFromDescription()
19+
.AllowAnonymous();
20+
21+
return app;
22+
}
23+
}

BankApi.Core/Defaults/Helper.JwsResponseSigningMiddleware.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
using Jose;
2-
using System.Security.Cryptography;
32

43
/// <summary>
54
/// <para> This middleware class signs the response body of each HTTP response using JSON Web Signature (JWS) with ECDSA.</para>
65
/// </summary>
76
public class JwsResponseSigningMiddleware
87
{
98
private readonly RequestDelegate _next;
10-
private readonly ECDsa _ecSigner;
9+
private readonly Jwk _jwk;
1110
private static readonly string[] headerCritValue = ["kid", "alg"];
1211
private static readonly string[] pathsToSkip = ["/scalar", "/openapi", "/health"];
1312

14-
public JwsResponseSigningMiddleware(RequestDelegate next, ECDsa ecSigner)
13+
public JwsResponseSigningMiddleware(RequestDelegate next, Jwk jwk)
1514
{
1615
_next = next;
17-
_ecSigner = ecSigner;
16+
_jwk = jwk;
1817
}
1918

2019
public async Task InvokeAsync(HttpContext context)
@@ -39,13 +38,13 @@ public async Task InvokeAsync(HttpContext context)
3938
{
4039
{ "crit", headerCritValue },
4140
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
42-
{ "kid", "bank-api-2025-1" }
41+
{ "kid", _jwk.KeyId }
4342
};
4443

4544
// Sign the response body using ECDSA
4645
string jws = JWT.EncodeBytes(
4746
responseBytes,
48-
_ecSigner,
47+
_jwk,
4948
JwsAlgorithm.ES512,
5049
extraHeaders,
5150
options: new JwtOptions { DetachPayload = true });

BankApi.Service.Beta/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using System.Security.Cryptography;
21
using Gridify;
32
using Microsoft.OpenApi.Models;
43

54
var builder = WebApplication.CreateBuilder(args);
6-
var ecSigner = ECDsa.Create(ECCurve.NamedCurves.nistP521); // Typically, you'd load this from a secure location and not create a new one each time
75

86
GlobalConfiguration.ApiDocument = builder.Configuration.GetRequiredSection("ApiDocument").Get<OpenApiDocument>()!;
97
GlobalConfiguration.ApiSettings = builder.Configuration.GetRequiredSection("ApiSettings").Get<GlobalConfiguration.SettingsModel>()!;
@@ -26,7 +24,8 @@
2624

2725
var app = builder.Build();
2826

29-
app.UseMiddleware<JwsResponseSigningMiddleware>(ecSigner);
27+
app.MapJwk(out var jwk); // register JWKS endpoint
28+
app.UseMiddleware<JwsResponseSigningMiddleware>(jwk);
3029
app.UseMiddleware<ApiVersionHeaderMiddleware>();
3130
app.UseExceptionHandler();
3231
app.UsePathBase(new($"/{GlobalConfiguration.ApiDocument.Info.Version}")); // Useful when versioning routing happens in an API Management system

BankApi.Service.Beta/apptests.http

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
###
99

10+
https://{{host}}/{{apiVersion}}/.well-known/jwks.json
11+
12+
###
13+
1014
https://{{host}}/{{apiVersion}}/health
1115
Ocp-Apim-Subscription-Key: Lifetime Subscription
1216

BankApi.Service.Stable/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using System.Security.Cryptography;
21
using Gridify;
32
using Microsoft.OpenApi.Models;
43

54
var builder = WebApplication.CreateBuilder(args);
6-
var ecSigner = ECDsa.Create(ECCurve.NamedCurves.nistP521); // Typically, you'd load this from a secure location and not create a new one each time
75

86
GlobalConfiguration.ApiDocument = builder.Configuration.GetRequiredSection("ApiDocument").Get<OpenApiDocument>()!;
97
GlobalConfiguration.ApiSettings = builder.Configuration.GetRequiredSection("ApiSettings").Get<GlobalConfiguration.SettingsModel>()!;
@@ -26,7 +24,8 @@
2624

2725
var app = builder.Build();
2826

29-
app.UseMiddleware<JwsResponseSigningMiddleware>(ecSigner);
27+
app.MapJwk(out var jwk); // register JWKS endpoint
28+
app.UseMiddleware<JwsResponseSigningMiddleware>(jwk);
3029
app.UseMiddleware<ApiVersionHeaderMiddleware>();
3130
app.UseExceptionHandler();
3231
app.UsePathBase(new($"/{GlobalConfiguration.ApiDocument.Info.Version}")); // Useful when versioning routing happens in an API Management system

BankApi.Service.Stable/apptests.http

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
###
99

10+
https://{{host}}/{{apiVersion}}/.well-known/jwks.json
11+
12+
###
13+
1014
https://{{host}}/{{apiVersion}}/health
1115
Ocp-Apim-Subscription-Key: Lifetime Subscription
1216

0 commit comments

Comments
 (0)