You are currently viewing Web API to Azure AD Protected Web API using MSAL

Web API to Azure AD Protected Web API using MSAL

Many organizations are now moving to Microservices and APIs calling other APIs is very common in Microservices architecture.

In this article, let’s see how a secured API can call another secured API, both secured using Azure AD.

Protected Web App and API

The Web App (.NET Core Web Application) will call the web API, both are protected using Azure AD. Let’s call this API as caller API in Azure app registration and while registering this API’s scope, name the scope as “caller-api“.

Refer my previous blog for creating and configuring these two projects.

Steps for creating a web app and web API

Create New Web API project

Create two web API projects using Visual Studio. Register both of them in Azure AD and get client id and tenant id. Configure the middleware and make both APIs secure.

Refer my previous blog article about securing web APIs for detailed steps.

Secure Web API using Azure AD

Complete Applications Setup

After following all above steps, we should have

  • a protected web application that calls one web API (let’s say caller API)
  • a protected caller API, which is protected using Azure AD and scope name is “caller-api“.
  • one more protected API project, which is also protected using Azure AD, but nobody calls it yet and the scope name is “access_as_user”

We will try to call this new API, from the caller API.

Azure AD App Registration

Now, let’s login to Azure Portal and go to the Caller API registration. We need to configure two more things in caller API app registration.

Client Secret

The caller API is calling another API, so it becomes confidential client application. So, we need to go to caller API's app registration and generate the client secret.

Copy this client secret and keep it at some place safe, we will need to add it to caller API's configuration file.

Client Secret for Caller API

Permission

Next, thing to add the permission to call the new API. From API Permissions in left navigation, select Add a permission and then select My APIs to see your newly registered API. Add this permission so that caller API can call the new API.

Add permission in caller API to call new API

Caller API Configurations

In the caller API configurations, we need to add the client secret. Below is the complete configuration file in my sampler caller API project. Just do not forget to update the client secret.

Caller API Startup

In the caller API, we need to ensure that the token of calling user is cached and used for the other API call. There are two main things which are required to be done here.

Startup Code

In startup of caller API, we need to add middleware for caching the incoming tokens. In Microsoft.Identity.Web 0.1.3 package, we can find an extension method, AddProtectedWebApiCallsProtectedWebApi, which abstracts all the details of validating token and adding it to cache.

Below snippet shows the completed startup.cs file and all middlewares are added there.

Controller Code

Then ITokenAcquisition instance can be used to get the token. This token can be added to Authorization header as bearer token and then we can call the new API.

Below snippet shows the code where ITokenAcquisition instance is injected in the controller. Then the same instance is used to get the cached token and injected in the HTTP request to the new API.

Web App Modifications

Initially, I assumed that it is logical to specify both the APIs scope in the web app, so that the token we get will be applicable for both the APIs.

So, I specified both access_as_user and caller-api scopes as shown below in startup.

Also, on the button click, added below code to call the caller-api, which also asked for scopes of both the APIs.

The Unexpected Error

Then when I tried to run the application, it threw an error. I was surprised to know that the scopes of both APIs can not be specified together.

MsalServiceException: AADSTS28000: Provided value for the input parameter scope is not valid because it contains more than one resource.

I could not find why the error was occurring from the documentation. But the error was stating that I have specified two resources and the scopes corresponding to our two APIs.

So, I thought may be two API scopes are not allowed in the initial scopes. But if they are not allowed in initial scopes requested for login, and if we just specify any one API scope, the token acquired will not have permissions to call the other API.

Failed Attempt To Resolve

To solve this issue, I thought I will remove the permission from startup file (from the snippet provided above) , but will keep both the permissions in the code in web app that calls the caller-api.

But this did not resolve the error. I was able to login successfully, and when I tried to call the API, I got the same error.

Successful Resolution

I went back to Azure Caller API registration and provided admin consent for default directory by clicking on the Grand admin consent for default directory button in caller-api’s app registration -> API Permissions screen.

I was sure that if I do this, it will provide the caller-api permission to call the other API, even though the user token has not requested that permission.

Updated startup file and the code that calls caller API, to only ask for the caller API scope.

The code which calls API from the web application was also updated as below:

Here the theory was if I get token which allows permission to call the caller api, then at least I will be able to login to the web application and will be able to issue call to it. The caller API would still be able to call

Run and Verify

I was successfully able to login in the web application and call the API which calls other API.

Successfuly call from web app to caller api to the other API

Did you find any other resolution instead of providing admin consent? Let me know your thoughts.

Leave a Reply