Azure: How to retrieve issuer-signing-key for validate-jwt policy? - azure

I'm working in Azure, where our Web App calls the APIM with the request, and then the APIM calls the WebApi to fulfill the request.
We're hitting a 500 error when we try to call one of the APIs set up, and when drilling down to the error in the browser (below is "Error message") (Inspect -> Network), it shows the error message coming from a validate-jwt policy set up for the apim (a product-public.policy.xml):
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Error message" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
<openid-config url="{{validauthorityissuer}}/.well-known/openid-configuration" />
<issuer-signing-keys>
<key>Base64 Encoded Key</key>
</issuer-signing-keys>
<audiences>
<audience>{{WebAppId}}</audience>
</audiences>
<issuers>
<issuer>{{validauthorityissuer}}</issuer>
</issuers>
</validate-jwt>
</inbound>
At first glance it looks like the issuer-signing-key is the problem, as it looks to be just a placeholder string.
To confirm it may be this causing the problem, I did a test in APIM, and got the following message:
{
"code": "401",
"type": "AAD Authorization",
"message": "No token provided to access the resource.",
"developerMessage": "Missing or badly formatted access token",
"moreInfo": null
}
This leads me to believe even more that the problem is the issuer-signing-keys.
My question is, where do I obtain the issuer-signing-key? Haven't been able to find much help online and through documentation.
Second question would be, assuming I get the key, would I have to convert it to base64, then paste it where it currently says "Base64 Encoded Key"?

you can get your issuer-signing-keys from your openID endpoint. I don't believe you have to worry about this. If you provide APIM with open-id endpoint (as you are doing) APIM will do this automatically. AD Samples below confirm this. You should remove the issuer related blocks. I would still experiment/validate.
https://learn.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies#ValidateJWT

Related

Azure APIM policy with dynamic claims in token validation

I am using APIM to expose multiple endpoints from a range of different Azure Functions as operations in the same API. Different operations in this API might have different audiences (aud) in the token based on which function is behind that particular operation.
What might differ though, is that the authorized party (azp) is not always the same. According to the OIDC documentation, this should then also be verified. In some cases, requests originate from a single page application where the token is issued on behalf of a user that signed in. In other cases, the request originates from someone integrating towards my APIs using a principal instead and a client_credentials grant type.
What I want to achieve is to have as simple a policy as possible, and still allow a range of different azp claims for a single aud claim. This is how I'm currently doing this:
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/{{tenantId}}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all">
<value>{{target-api-clientId}}</value>
</claim>
<claim name="azp" match="any">
<value>{{spa-clientId}}</value>
<value>{{integrator-clientId1}}</value>
<value>{{integrator-clientId2}}</value>
</claim>
</required-claims>
</validate-jwt>
However, this doesn't scale very well and forces me to keep duplicate policies between environments and for each new allowed authorized party I must add a new row in the azp claim section as a well as new named values.
The documentation specifies that there is a separator attribute for the claim element, but as far as I can tell, this only splits the claim in the incoming token on that separator character and I always only have a single aud and azp claim in the incoming tokens.
Is there any way for me to achieve a single API baseline policy, where each operation could set its expected required parameters? Something along these lines:
API base policy
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/{{tenantId}}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all">
<value>#((string)context.Variables["aud"])</value>
</claim>
<claim name="azp" match="any">
#((List<string>)context.Variables["azp"])
</claim>
</required-claims>
</validate-jwt>
Operation specific policy
<set-variable name="aud" value="{{allowed-aud-for-this-operation}}" />
<set-variable name="azp" value="{{list-of-allowed-authorized-parties-for-this-operation}}" />
<base />
To further clarify what the infrastructure looks like and where requests might originate from, this might shine some light:
Is there any way to achieve dynamic token evaluation like this? If not, what are my other options? Am I approaching this problem the right way, or should I rethink?

Multiple Authentication policies in Azure APIM

I have two scenarios where in first client will call the APIM with bearer token and other one is Function App will call our APIM with Managed identity. I have the code which will validate the jwt. but I want to know how can I skip the other one if either one is available(e.g I want to skip the jwt validation if its get called with managed Identity). From examples I can see I can do a choose and when but not sure what will be the headers for the managed identity. Here is what I am thinking I should be updating.
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("Authorization","") != "")">
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Invalid or Expired token" require-expiration-time="true" require-signed-tokens="true">
<openid-config url=".well-known/openid-configuration" />
<audiences>
<audience>audience</audience>
</audiences>
<issuers>
<issuer>issuer</issuer>
</issuers>
</validate-jwt>
</when>
<when condition="to validate managed identity">
<authentication-managed-identity resource="resource" client-id="clientid of user-assigned identity" output-token-variable-name="token-variable" ignore-error="true|false"/>
</when>
</choose>
For this requirement, I think there isn't any difference between the two request(request APIM with bearer token directly and request APIM from function app by managed identity). Both of them will provide a bearer token in header(Authorization) of the request.
You can refer to this document about calling APIM from function app according to managed identity. You can find the lines of code shown as below in the document which are used to get access token and set the token in header when request APIM.
var azureServiceTokenProvider = new AzureServiceTokenProvider(identity);
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(target,tenantID);
wc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
So the second request scenario is same with the first request scenario. We can not distinguish them in APIM policy before <validate-jwt>. To implement your requirement, I think you can add a property into the header of the two requests and then check the header in APIM policy. For example, in function app code, you can add a header like below:
httpClient.DefaultRequestHeaders.Add("comeFrom", "fromFunApp");
Then check the header in policy:
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("comeFrom","") != "fromFunApp" && context.Request.Headers.GetValueOrDefault("Authorization","") != "")">
..............

Use Azure App Service Authentication to log into Api Management Service

I was able to setup Azure Active Directory (aad) to authenticate users for the App Service running an angular front-end, now I want to secure the backend by allowing only this authenticated users to send requests to the Api Management Service endpoint.
So, I followed this article https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad and I am facing 2 major problems:
The /.auth/me endpoint only returns an id_token, not an access_token
When I try with postman, I keep on getting invalid audience, but postman uses an audience that looks like 00000-00000...
Here's the api management service inbound request policy jwt-validate:
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="failed message">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration" />
<audiences>
<audience>{app-id-uri}></audience>
</audiences>
</validate-jwt>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
For your first problem:
Actually, it can just response id token when you request the /.auth/me endpoint because you just implement login but not specify a resource(or a backend app/api protected by AD) in your app service. So the response contains id token and without access token.
If you want to get access token when you request /.auth/me endpoint, you need to specify the resource in your app service. Here I assume you have already registered an app in Azure AD to represent your APIM. Then you can use the Resource Explore to modify the settings like below:
1 . locate your app service
2 . locate the config->authsettings (the resource below is the client id of the app you registered to represent your APIM)
"additionalLoginParams": [
"response_type=code id_token",
"resource=3fa9607b-63cc-4050-82b7-91e44ff1df38"
],
3 . config the redirect_uri for Azure app like below:
https://yourapp.azurewebsites.net/.auth/login/aad/callback
Then after you login in the angular app, you can get the access_token via the endpoint:
https://yourapp.azurewebsites.net/.auth/me
The steps above come from this post, you can also refer to it for detail information.
For your second problem:
When you request the APIM api in postman, you should put the access token in the request header. And you mentioned that the access token which you used in postman contains the audience like 00000-00000... But in your validate-jwt policy, you use:
<audiences>
<audience>{app-id-uri}</audience>
</audiences>
It seems {app-id-uri} is not only a id but should be a uri like: api://00000-00000.... So please change {app-id-uri} from uri format to id format (just remove api://).

Azure API Management: authorization with Oauth2 401 gives "Unauthorized. Access token is missing or invalid."

I have a few APIs (Logic Apps, Functions) that I want to expose through Azure API Management.
They work fine, so I decided to add OAuth2 autorization.
I followed step by step https://learn.microsoft.com/fr-fr/azure/api-management/api-management-howto-protect-backend-with-aad:
Register an application (backend-app) in Azure AD to represent the API.
Register another application (client-app) in Azure AD to represent a client application that needs to call the API.
In Azure AD, grant permissions to allow the client-app to call the backend-app.
Configure the Developer Console to call the API using OAuth 2.0 user authorization.
Add the validate-jwt policy to validate the OAuth token for every incoming request.
Also use Postman to test
Everything works until the "validate-jwt" policy step.
When I add it, I get a "401 - Unauthorized. Access token is missing or invalid."
I can get the token, in Developer Console and Postman, but as soon as I do the API call... 401!
When I used jwt.ms to check the content of the token, I noticed that the aud param has nothing to do with the backend Application ID.
The value in the token is "00000003-0000-0000-c000-000000000000", whereas the backend app ID is like "16caXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXc0".
I'm running out of ideas and need the help of some Azure gurus out there!
Help would be very much appreciated...
Here below the inbound policy as per the MS doc:
<policies>
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
<openid-config url="https://login.microsoftonline.com/MY_AD_TENANT_ID/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>MY8BACKEND_APP_ID_GUID</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
<backend>
<forward-request />
</backend>
<outbound />
<on-error />
</policies>
Screen cap of the Postman screen where I get the token (this works, but then when I send the request --> 401)
Screen cap of aud param in jwt.ms
I had some problems with validating Azure AD tokens a couple of years back - see my write up.
I suspect the problem is the nonce in the JWT header.
You're not really required to check value of aud parameter. You could remove required-claims alltogether, this way token presence and signature would still be validated. If you want to make sure that token was issued for your app, just find the claim that contains app id and use it in name="..." to match against your app id value.
if using v2 version endpoint, go to -> azure ad -> app registration -> select backend-app -> manifest -> update property "accessTokenAcceptedVersion": 2,"

JWT validation failure error in azure apim

I am currently trying to implement Oauth2.0 to protect API using below documentation
https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad
And currently using the DEMO CONFERENCE API provide by azure apim to test the implementation.
And currently receiving error during test in developer portal as :
"message": "JWT Validation Failed: Claim value mismatch: aud=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx.."
Compared the token passed with the claim value by decoding it and its matching.
I have the jwt token validation policy as below
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-expiration-time="false" require-signed-tokens="false">
<openid-config url="https://login.microsoftonline.com/xxxxxxxxx-07c8-xxxxx-xxxx-xxxxxxxxx/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all" separator="-">
<value>xxxxxxxx-xxxxx-489e-a26e-xxxxxxxx</value>
</claim>
</required-claims>
</validate-jwt>
</inbound>
First, you need to validate your JWT token. Then when we register an application its getting registered with version V1 and Access token issuer comes with sts url and if we try to pass Access Token with V2 its failed V2 issuer is login.microsoft.com.
So fix is to go in manifest file "accessTokenAcceptedVersion": 2 for registered applications in AD. Refer to this issue.

Resources