From 1b973fe15c07f3136ef73992f4d7d63d6e92176c Mon Sep 17 00:00:00 2001 From: Jai Radhakrishnan <55522316+jairad26@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:33:47 -0800 Subject: [PATCH 1/5] add support for multiple jwks keys per endpoint --- runtime/middleware/jwt.go | 46 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/runtime/middleware/jwt.go b/runtime/middleware/jwt.go index f4ce84b2..69ac9436 100644 --- a/runtime/middleware/jwt.go +++ b/runtime/middleware/jwt.go @@ -205,34 +205,44 @@ func jwksEndpointsJsonToKeys(ctx context.Context, jwksEndpointsJson string) (map return nil, err } keys := make(map[string]any) - for key, value := range jwksEndpoints { + for endpointKey, value := range jwksEndpoints { jwks, err := jwk.Fetch(ctx, value) if err != nil { return nil, err } - jwkKey, exists := jwks.Key(0) - if !exists { - return nil, errors.New("No keys found in JWKS for key: " + key) + if jwks.Len() == 0 { + return nil, errors.New("No keys found in JWKS for endpoint: " + endpointKey) } - var rawKey any - err = jwkKey.Raw(&rawKey) - if err != nil { - return nil, err - } + for it := jwks.Keys(ctx); it.Next(ctx); { + jwkKey := it.Pair().Value.(jwk.Key) + var rawKey any + if err := jwkKey.Raw(&rawKey); err != nil { + logger.Warn(ctx).Err(err).Msg("Failed to get raw key for endpoint " + endpointKey) + continue + } - // Marshal the raw key into DER-encoded PKIX format - derBytes, err := x509.MarshalPKIXPublicKey(rawKey) - if err != nil { - return nil, err - } + // Marshal the raw key into DER-encoded PKIX format + derBytes, err := x509.MarshalPKIXPublicKey(rawKey) + if err != nil { + logger.Warn(ctx).Err(err).Msg("Failed to marshal key for endpoint " + endpointKey) + continue + } - pubKey, err := x509.ParsePKIXPublicKey(derBytes) - if err != nil { - return nil, err + pubKey, err := x509.ParsePKIXPublicKey(derBytes) + if err != nil { + logger.Warn(ctx).Err(err).Msg("Failed to parse key for endpoint " + endpointKey) + continue + } + + // Use a combination of endpoint key and key ID (if available) as the map key + keyID := endpointKey + if kid, exists := jwkKey.Get("kid"); exists { + keyID = endpointKey + "_" + kid.(string) + } + keys[keyID] = pubKey } - keys[key] = pubKey } return keys, nil } From c81ec617db2b0e228bab196bf72d7579c17ac986 Mon Sep 17 00:00:00 2001 From: Jai Radhakrishnan <55522316+jairad26@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:36:30 -0800 Subject: [PATCH 2/5] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff22b3aa..01c5d869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## 2025-01-24 - Runtime 0.17.1 - fix: separate modusdb instance by app [#729](https://github.com/hypermodeinc/modus/pull/729) +- fix: jwks endpoint should use key ID if available + [#730](https://github.com/hypermodeinc/modus/pull/753) ## 2025-01-24 - Runtime 0.17.0 From c50f450204f0a634e81874ab532b3cdaaa302a87 Mon Sep 17 00:00:00 2001 From: Jai Radhakrishnan <55522316+jairad26@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:38:16 -0800 Subject: [PATCH 3/5] . --- runtime/middleware/jwt.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/runtime/middleware/jwt.go b/runtime/middleware/jwt.go index 69ac9436..fa6c72c0 100644 --- a/runtime/middleware/jwt.go +++ b/runtime/middleware/jwt.go @@ -219,21 +219,18 @@ func jwksEndpointsJsonToKeys(ctx context.Context, jwksEndpointsJson string) (map jwkKey := it.Pair().Value.(jwk.Key) var rawKey any if err := jwkKey.Raw(&rawKey); err != nil { - logger.Warn(ctx).Err(err).Msg("Failed to get raw key for endpoint " + endpointKey) - continue + return nil, err } // Marshal the raw key into DER-encoded PKIX format derBytes, err := x509.MarshalPKIXPublicKey(rawKey) if err != nil { - logger.Warn(ctx).Err(err).Msg("Failed to marshal key for endpoint " + endpointKey) - continue + return nil, err } pubKey, err := x509.ParsePKIXPublicKey(derBytes) if err != nil { - logger.Warn(ctx).Err(err).Msg("Failed to parse key for endpoint " + endpointKey) - continue + return nil, err } // Use a combination of endpoint key and key ID (if available) as the map key From 9467d9bb0e17591a3a744671ab1ea8305b1a657e Mon Sep 17 00:00:00 2001 From: Jai Radhakrishnan <55522316+jairad26@users.noreply.github.com> Date: Fri, 7 Feb 2025 08:39:49 -0800 Subject: [PATCH 4/5] . --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 150b9c1a..ad599a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,11 @@ - fix: CORS: allow all request headers [#741](https://github.com/hypermodeinc/modus/pull/741) - fix: accept long base64 strings [#742](https://github.com/hypermodeinc/modus/pull/742) - fix: improve dgraph auth header passing [#752](https://github.com/hypermodeinc/modus/pull/752) +- fix: jwks endpoint should use key ID if available [#730](https://github.com/hypermodeinc/modus/pull/753) ## 2025-01-24 - Runtime 0.17.1 - fix: separate modusdb instance by app [#729](https://github.com/hypermodeinc/modus/pull/729) -- fix: jwks endpoint should use key ID if available - [#730](https://github.com/hypermodeinc/modus/pull/753) ## 2025-01-24 - Runtime 0.17.0 From 65033e0b812a0b2a9c5868ed3787dcd462afb979 Mon Sep 17 00:00:00 2001 From: Jai Radhakrishnan <55522316+jairad26@users.noreply.github.com> Date: Fri, 7 Feb 2025 09:42:19 -0800 Subject: [PATCH 5/5] . --- runtime/middleware/jwt.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/middleware/jwt.go b/runtime/middleware/jwt.go index fa6c72c0..2f41cdac 100644 --- a/runtime/middleware/jwt.go +++ b/runtime/middleware/jwt.go @@ -211,10 +211,6 @@ func jwksEndpointsJsonToKeys(ctx context.Context, jwksEndpointsJson string) (map return nil, err } - if jwks.Len() == 0 { - return nil, errors.New("No keys found in JWKS for endpoint: " + endpointKey) - } - for it := jwks.Keys(ctx); it.Next(ctx); { jwkKey := it.Pair().Value.(jwk.Key) var rawKey any