Dynamic 'kid' Usage in Azure APIM Validate-JWT Policy
We currently use hardcoded exponent and modulus values within the
Azure API Management
Azure Policy
-
Khadeer Ali 1,790 Reputation points • Microsoft Vendor
2024-12-19T18:39:15.78+00:00 Welcome to the Microsoft Q&A Platform!
Thank you for reaching out about the dynamic JWT validation issue in Azure API Management (APIM). Upon review, it appears the error you encountered—"Exactly one of a value or certificate-id or n/e pair must be specified for the issuer signing key"—is caused by a missing modulus (n) and exponent (e) pair in the <issuer-signing-keys> element.Issue and Resolution:
- In your current implementation, you are correctly fetching the modulus (n) and exponent (e) for kid1 from the JWKS endpoint. However, the error occurs because the policy expects either a key element containing n and e, or a certificate-id for validation.
- The solution might be to modify the
<issuer-signing-keys>
element to include themodulus
andexponent
values forkid1
directly, as you’ve already fetched them dynamically.<issuer-signing-keys> <key n="@((string)context.Variables["modulus1"])"e="@((string)context.Variables["exponent1"])" /> </issuer-signing-keys>
By ensuring that the <issuer-signing-keys> element includes the required modulus and exponent for the corresponding kid, the validation should work as expected.
Let me know if you need any further clarification or assistance with this.
-
Aaron Bowe 0 Reputation points
2024-12-20T13:54:55.2666667+00:00 Thanks for your reply.
We initially attempted what you suggested (using the exponent and modulus) but were not successful. We believe that was because, according to Microsoft’s documentation, policy expressions aren’t allowed for the exponent or modulus. That’s why we’re attempting to get this to work with the ‘kid’. Are we misinterpreting this documentation.
-
Aaron Bowe 0 Reputation points
2024-12-23T14:33:32.7966667+00:00 I've found this will work. Do you have any suggestions for improvements?
<policies>
<inbound> <base /> <send-request mode="new" response-variable-name="jwksResponse" timeout="10" ignore-error="true"> <set-url>https://evkeys.xxxxxxxxx.com/auth/jwks</set-url> <set-method>GET</set-method> </send-request> <!-- Get the two kid values from the JWT and convert them to base64 --> <set-variable name="jwks" value="@((JObject)((IResponse)context.Variables["jwksResponse"]).Body.As<JObject>())" /> <set-variable name="kid1" value="@((string)((JObject)context.Variables["jwks"])["keys"][0]["kid"])" /> <set-variable name="kid2" value="@((string)((JObject)context.Variables["jwks"])["keys"][1]["kid"])" /> <set-variable name="base64kid1" value="@{ var kid1 = context.Variables.GetValueOrDefault<string>("kid1"); var bytes = System.Text.Encoding.UTF8.GetBytes(kid1); return System.Convert.ToBase64String(bytes); }" /> <set-variable name="base64kid2" value="@{ var kid2 = context.Variables.GetValueOrDefault<string>("kid2"); var bytes = System.Text.Encoding.UTF8.GetBytes(kid2); return System.Convert.ToBase64String(bytes); }" /> <validate-jwt header-name="Authorization" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true" output-token-variable-name="xxxxxxxxx-jwt-token"> <issuer-signing-keys> <key>@((string)context.Variables["base64kid1"])</key> <key>@((string)context.Variables["base64kid2"])</key> </issuer-signing-keys> <issuers> <issuer>https://auth.xxxxxxxxx.com</issuer> </issuers> <required-claims> <claim name="exp" match="all" /> <claim name="iss" match="all" /> <claim name="x_query_parameter" match="all"> <value>@(System.Convert.ToBase64String(Encoding.UTF8.GetBytes((string)context.Request.Url.QueryString.TrimStart('?'))))</value> </claim> <claim name="x_target_client" match="all"> <value>{{ev-non-prod-dev-x-target-client}}</value> </claim> </required-claims> </validate-jwt> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error>
</poli
-
Khadeer Ali 1,790 Reputation points • Microsoft Vendor
2024-12-23T17:34:00.53+00:00 @Aaron Bowe
Thank you for sharing your solution. It’s great to see this approach leveraging send-request to dynamically retrieve the JWKS and generate the keys for validation. The use of dynamically fetching kid values, encoding them to Base64, and passing them into the <validate-jwt> policy aligns with the requirement to dynamically handle key validation. -
Deleted
This comment has been deleted due to a violation of our Code of Conduct. The comment was manually reported or identified through automated detection before action was taken. Please refer to our Code of Conduct for more information.
-
Aaron Bowe 0 Reputation points
2025-01-03T14:41:44.6+00:00 I was wrong when I stated that the above inbound policy was working. My test JWT was missing the ‘kid’ value from the JWT header and it appears that the issuer-signing-keys were not used for validation.
This is the error message provided from Azure APIM trace when using a properly formatted JWT (some values partially masked).
{ "message": "JWT Validation Failed: IDX10503: Signature validation failed. The token's kid is: 'accb04ff-xxxx-xxxx-8d07-dc6b9bd5b8b3', but did not match any keys in TokenValidationParameters or Configuration. Keys tried: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'Aa0w14qR7PxxxxxxxxxxxxxxxxxxxxxxxxYSPaGf92w'. , KeyId: \r\nMicrosoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'N1Ua8mkpNAxxxxxxxxxxxxxxxxxxxxxxxxxpTLZCcetc'. , KeyId: \r\n'. Number of keys in TokenValidationParameters: '2'. \nNumber of keys in Configuration: '0'. \nExceptions caught:\n 'System.NotSupportedException: IDX10634: Unable to create the SignatureProvider.\nAlgorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'Aa0w14qR7PJIxxxxxxxxxxxxxxxxxxxxxxxxxxxxPaGf92w'.'\n is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms\r\n at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)\r\n at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm, Boolean cacheProvider)\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, SecurityToken securityToken, TokenValidationParameters validationParameters)\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)\r\nSystem.NotSupportedException: IDX10634: Unable to create the SignatureProvider.\nAlgorithm: 'RS256', SecurityKey: 'Microsoft.IdentityModel.Tokens.SymmetricSecurityKey, KeyId: '', InternalId: 'N1Ua8mkpxxxxxxxxxxxxxxxxxxxxxxxxxpTLZCcetc'.'\n is not supported. The list of supported algorithms is available here: https://aka.ms/IdentityModel/supported-algorithms\r\n at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures, Boolean cacheProvider)\r\n at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForVerifying(SecurityKey key, String algorithm, Boolean cacheProvider)\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, SecurityToken securityToken, TokenValidationParameters validationParameters)\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration)\r\n'.\ntoken: 'hidden'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/SecurityArtifactLogging.]'. See https://aka.ms/IDX10503 for details.." }
I assume the error message is indicating the issuer-signing-key has not been implemented properly. Do you agree? Maybe I’m going about this entirely wrong. I need help figuring out how to use the ‘kid’ to validate the JWT.
<issuer-signing-keys>
<key>@((string)context.Variables["base64kid1"])</key>
<key>@((string)context.Variables["base64kid2"])</key>
</issuer-signing-keys>
Sign in to comment