Migrating from validate-jwt to validate-azure-ad-token policy
Asked Answered
C

3

5

I've previously been using the validate-jwt policy successfully in a lot of projects. I would like to try out the new validate-azure-ad-token policy instead.

I changed one of my policies from:

<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/{{tenant-id}}/v2.0/.well-known/openid-configuration" />
    <required-claims>
        <claim name="azp" match="all">
            <value>{{portal-clientId}}</value>
        </claim>
    </required-claims>
</validate-jwt>

To:

<validate-azure-ad-token header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." tenant-id="{{tenant-id}}">
    <client-application-ids>
        <application-id>{{portal-clientId}}</application-id>
    </client-application-ids>
</validate-azure-ad-token>

The former works without any issues, but I just can't get the latter to work. The named value portal-clientId in both policy snippets above correspond to the applicationId of both the app registration and the enterprise app.

As you can see from the former, the value is in the azp claim. From jwt.ms, it sounds like this should be the correct claim for the new policy as well:

The application ID of the client using the token. The application can act as itself or on behalf of a user. The application ID typically represents an application object, but it can also represent a service principal object in Azure AD.

I've read both the Azure documentation and the article introducing the new feature, but I just can't figure out what I'm doing wrong. When tracing, the newer policy has the following to say:

validate-azure-ad-token (3.470 ms)
{
    "message": "Azure AD JWT Validation Failed: ValidationFailed."
}

Are there some hidden expectations in the new policy? My issuer in the token is https://login.microsoftonline.com/{{tenant-id}}/v2.0.

Viewing logs in application insights connected to this API yields no more useful information than the trace, even when activating verbose logging.

Any help would be greatly appreciated.

Claver answered 9/12, 2022 at 8:18 Comment(1)
You can use API inspector to troubleshoot this: learn.microsoft.com/en-us/azure/api-management/…Daliadalila
N
4

(Source: I am on the Azure API Management team and responsible for the APIM policy implementations).

Short version: Revert back to using validate-jwt (for now).

There are two issues here.

  1. Your two statements are not equivalent. The client application ID is exposed in JWT as an audience. You are correct on the validate-azure-ad-token version and wrong on the validate-jwt version.
  2. There is a current bug (that we are working on and will be fixed "soon") whereby the underlying library we are using for Azure Active Directory is not validating v2.0 tokens.
Negligee answered 20/12, 2022 at 16:15 Comment(5)
Thank you for the response! I think I'm a bit confused about the terminology for the validate-azure-ad-token then, as it has a section for audience as well, but that is not required whereas client-application-ids is required. I don't particularly care about the client in this case, because I know if it can receive a token it has the right to access my APIs. As far as I understand, aud is always what is most important since that determines the receiving end, in this case my APIs backend. I am using 2.0 tokens so reverted to validate-jwt.Claver
I suspect you are taking a short cut around the in-built security for the JWT. When using Azure AD, you would create two applications - one for the service (which exposes an API) and another for the client (which has permission to access the API). When you are doing either a validate-jwt or validate-azure-ad-token, you want to check that the JWT provided by the client is correct without saying anything about what APIs are available. Authorization should remain with the backend service. In API Management, you are just saying "I expect this client to be accessing this API"Negligee
I have >2 principals, one representing a single page application and additionally for multiple different APIs all exposed in APIM. Different APIs validate different audiences, but the client is the same in almost all cases (my SPA, different based on environment only). I then assign access to users to the SPA principal using roles I have defined both on the SPA and API principals. I then know that to get a token for any of the APIs, the user must have been assigned. Am I doing something wrong in my setup here?Claver
How does this work when you have many client applications calling a common API. Do you have to list all of the client IDs from the calling applications?!Indiscrimination
@Indiscrimination yes, just a list of allowed client applications (azp claims)Claver
M
1
After spending more than 4 hours trying to figure the the correct Policy finally it works.
The policy should be as follows:-

  <validate-azure-ad-token tenant-id="521c22d3-fb21-4c0f-8ff6-*****">
            <audiences>
                <audience>api://29f66c25-56c9-474f-b298-******</audience>
            </audiences>
            <client-application-ids>
                <application-id>7d2e7430-bc1b-44e6-8f63-*****</application-id>
            </client-application-ids>
        </validate-azure-ad-token>

You can extract the application ID and audience from the token, just take any generated token and decode it.

Marchak answered 24/8, 2023 at 14:1 Comment(0)
S
1

I am trying to answer your question.

You got:

"message": "Azure AD JWT Validation Failed: ValidationFailed."

The root cause was you called the wrong token endpoint. Let me explain why.

In Azure API management service, there are two Inbound Processing policies:

  • validate-azure-ad-token
  • validate-jwt

They serve OAuth 2.0 authorization client credentials flow, but with different use cases.

Use 'validate-azure-ad-token' Policy

Use Case: Your backend application (Resource Server) has enabled authentication, you need API management to validate access token at API gateway. If access token validation fails, don't even forward the request to the backend app. In this case, you only need to register a single application, "Resource-App". From the resource-app, you can get the token endpoint (V2), and POST call the token endpoint to get the access token.

API management receives the access token from the client-app, and it validates the access token, then forwards the access token to the resource server. The resource server receives the access token, and performs authentication with configured Microsoft identity provider. Of course, you can configure different identity provider, such as Google, GitHub, etc. Once the client-app's access token is validated, the resource server returns the requested resource to the calling client-app. In our case, I used App Service as the Resource Server.

Here is the diagram to showcase the flow.

enter image description here

Prerequisites

  • Use App Registration to register an App, say, it's resource-app.
    • In resource-app, expose an API, you should get Application ID URI: in the format: api://xxxx-xxxx-xxx
  • API Management, add validate-azure-ad-token inbound policy.
<validate-azure-ad-token tenant-id="xxx">
  <audiences>
    <audience>{{Application_ID_URI}}</audience>
  </audiences>
  <client-application-ids>
    <application-id>{{client_id}}</application-id>
  </client-application-ids>
</validate-azure-ad-token>
  • In your Resource Server, configure Authentication. I use App Service as the resource server. It can be any valid compute resource applications. Note, this resource server is different from the resource-app we registered earlier. In OAuth 2.0, it's called the Resource Server. It can be any REST API service, or a Web Application.
    • In App Service, select Authentication
    • Authentication settings, make sure set as follows if it's a REST API application.
      • Unauthenticated requests: Return HTTP 401 Unauthorized
    • Add Identity Provider: Microsoft
    • Follow the steps to configure it. Use client_id, and client_secret from the resource-app when asked.
    • Client secret setting name: MICROSOFT_PROVIDER_AUTHENTICATION_SECRET
    • Allowed token audiences: {Application ID URI}
    • Rest of the configuration, use the default.
    • Once it's done, your App Service app should be protected by OAuth 2.0 client credentials grant type flow. You can test it to see if unauthorized access is allowed or not. It should block any unauthorized access from now on.

Process

Step 1:

  • In your client-app (client testing tool, postman, for example), make a POST call with the following key-value pairs in the body. All the information below is from the resource-app:
    • client_id: xxx
    • client_secret: xxx
    • grant_type: client_credentials
    • scope: api://xxxx-xxxx-xxx/.default
  • The URL for the POST call is the TOKEN ENDPOINT from the resource-app. Make sure you use the OAuth 2.0 token endpoint (v2)
  • You should receive an access_token.

Step 2:

  • Copy the access_token from step 1
  • In your client tool, create a GET call request to the API Management URL.
  • In the request header, add Authorization:
    • Key: Authorization
    • Value: Bearer <access_token>
  • If everything goes well, you should receive a valid response from the resource server - App Service.

Use 'validate-jwt' Policy

Use Case: You want API Management to handle OAuth 2.0 client credentials grant flow entirely without the backend application (Resource Server) involved. In this case, you need to register 2 applications:

  • client-app
  • resource-app

When getting access token, you need to call client-app's TOKEN ENDPOINT. To protect the Resource Server, you'd better make it private access in Azure depending on the type of compute resource you used.

Resource-App

  • Register a "Resource-App"
  • Expose an API
  • Create App Role

Client-App

  • Register a "Client-App"
  • Configure API Permissions
  • Make sure you add a permission for the exposed API from the resource-app
  • Make sure Admin consent required is granted. This is very important. Without it, you can't make it work. In an enterprise environment, you might need to create a ticket for Microsoft Graph, and ask for:
    • User.ReallAll
    • User.WriteAll

API Management

  • Make sure to add a 'validate-jwt' Policy as shown below.
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized" require-expiration-time="true" require-scheme="Bearer" require-signed-tokens="true">
  <openid-config url="https://login.microsoftonline.com/{{Tenant-ID}}/v2.0/.well-known/openid-configuration" />
  <required-claims>
     <claim name="aud" match="any">
       <value>{{Application_ID_URI}}</value>
     </claim>
  </required-claims>
</validate-jwt>
  • API Management will not forward the access token to the resource server (App Service). It just validates the access token. Return Unauthorized if it fails.

Client Tool for testing (Postman or the like)

  • Get access token from the client-app TOKEN ENDPOINT using POST request. Please note, don't use the token endpoint from the resource-app in this case.
  • Use the access token to invoke APIM endpoint URL.
  • Receive the response from the resource server - App Service in our case.

Resource Server - App Service

  • Since we did not configure Authentication, the App Service is not protected by OAuth 2.0.
  • To work around this, you can deploy your App Service with Private Access, or use virtual network injection to make your App Service as private. So in this way, it can be secured.

Attached a diagram to showcase 'validate-jwt' Policy

enter image description here

Summary

Hope you understand the difference between the two policies at this point.

In short, the preferred Azure APIM inbound processing policy for OAuth 2.0 is the validate-azure-ad-token policy. It's simple because it does not require the registered application to have "Admin consent" when configuring API permission.

Here is the OAuth 2.0 Authorization in Azure Cloud diagram I drew for overall architecture design and clarification. And good luck.

enter image description here

Welcome any feedback!

Sine answered 29/9, 2024 at 7:10 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.