Azure APIM policy with dynamic claims in token validation - azure

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?

Related

Using Managed Identity to call APIM endpoint

I am trying to make a call to the APIM endpoint in Azure from the function app using the Managed Identity of the function app. Not sure if there is any article I could take a reference from?
I believe you can do it using validate-jwt policy. The policy will look like 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/{aad-tenant}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>{backend-api-application-client-id}</value>
</claim>
</required-claims>
</validate-jwt>
more info: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad#configure-a-jwt-validation-policy-to-pre-authorize-requests
You should apply this policy type: https://learn.microsoft.com/en-us/azure/api-management/api-management-authentication-policies#ManagedIdentity

Azure APIM Validate JWT based on two different claims

I am trying to create a policy in Azure APIM to validate JWT Tokens (Azure AD tokens) based on two different claims.
My API may be consumed by either other applications or users - it may be called from an user context or an application context. As such, the token might contain either the "scp" claim e.g. user_impersonation or "roles" claim like [ "CallApiAsAnApp" ].
From the documentation, I have not found any way to do this. Is this possible or do I have to implement custom code to do this at the API level? Any claims added to "required-claims" in the policy become mandatory. There doesn't seem to be any "match-any" option at the claims level, only values level.
In the end, the only way I could find to fix the issue is to use the choose element instead of validate-jwt, with custom code (which is what I was hoping to avoid any way). Basically I am saving the validated JWT Token to a variable and then using if / elseif to determine if the token is acceptable. Working relevant configuration below
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" require-scheme="Bearer" output-token-variable-name="valid-jwt">
<openid-config url="https://login.microsoftonline.com/tenantid/v2.0/.well-known/openid-configuration" />
<issuers>
<issuer>https://sts.windows.net/tenantid/</issuer>
</issuers>
</validate-jwt>
<choose>
<when condition="#{
var jwt = (Jwt)context.Variables["valid-jwt"];
if(jwt.Claims.ContainsKey("roles")){
var roles = jwt.Claims["roles"];
return !Array.Exists(roles, element => element == "MyRoleName");
} else if (jwt.Claims.ContainsKey("scp")){
var scp = jwt.Claims["scp"];
return !Array.Exists(scp, element => element == "user_impersonation");
} else { return true; }
}">
<return-response>
<set-status code="401" reason="Unauthorized" />
</return-response>
</when>
<otherwise />
</choose>
As you mentioned the token may contain either the scp cllaim or roles claim, it seems your token sometimes generated in "Delegated" type and sometimes generated in "Application" type. You just need to configure the <validate-jwt> policy like below screenshot, add both of the claims in it and choose "Any claim".
After that, the token can be validated if it just contain one claim.
By the way, please check if your tokens in two situations have same "Audiences", otherwise your requirement may not be implemented by the configuration above.

What is the the openid-config url for a token generated by Azure Devops?

I'm trying to create an Azure API Management policy to validate JWT token. This token comes from our Azure Devops release pipeline. Does anyone know what the correct openid-config url is?
Here is a sample I took out from microsoft docs:
<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/contoso.onmicrosoft.com/.well-known/openid-configuration" />
<audiences>
<audience>25eef6e4-c905-4a07-8eb4-0d08d5df8b3f</audience>
</audiences>
<required-claims>
<claim name="id" match="all">
<value>insert claim here</value>
</claim>
</required-claims>
</validate-jwt>
As far as I know, the openid-config url is independent of whether it came from devops.
If you use Azure AD, you can use https://login.microsoftonline.com/{aad-tenant}/.well-known/openid-configuration(for v1 endpoint) as openid-config url. And you can also use https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration(for v2 endpoint) as openid-config url. You can refer to this document.
But if you don't use Azure AD, you need to find the openid-configuration by yourself.

Azure Apim authentication for apis

I have an api endpoint which is being used by multiple public clients. And each client is configured with different clientids. I am using Authorization Code flow with Oauth2 openid protocol. I would like to introduce an Azure Apim service and would like to validate the jwt token before forwarding the request to apis. Could you please let me know how can i achieve this?
Note: I can't use single client id as redirect uri is different for each client.
You can use the Validate JWT policy to pre-authorize requests in API Management, by validating the access tokens of each incoming request. If a request does not have a valid token, API Management blocks it. For example, add the following policy to the <inbound> policy section of the Echo API. It checks the audience claim in an access token, and returns an error message if the token is not valid.
<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/{aad-tenant}/.well-known/openid-configuration" />
<required-claims>
<claim name="aud">
<value>{Application ID of backend-app}</value>
</claim>
</required-claims>
</validate-jwt>
For more details, you could refer to the article about Configure a JWT validation policy to pre-authorize requests

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