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.
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:
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
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.
Welcome any feedback!