Options for obtaining an access token with Azure application to application authentication
In this post we will take a look at a few different options that we can use, to get an access token so that a daemon application (say client) can communicate with another application (say server). Both client and server applications can be developed as Azure app services and/or Azure function. In this post we will be sticking with Azure functions for both client and server where needed.
Setup
Let’s assume the following setup (in the same tenant) for this post:
- Client runs on a timer (or it receives requests from end users).
- Client app runs code to get an access token and calls the server app for data (say Orders).
- The server app receives requests only from apps (not users) and expects certain claims to be present in the request.
- The server app is secured using an app registration.
- Based on the request received, the serve app does some computation and returns the data (Orders) to the client app.
- If applicable, the client app then returns that data to end users.
In this blog post we will be focusing on points 2, 3 and 4 above which are related to the way the client app sends requests to the server app. An HttpClient object will be used by the client app to call the server app. This HttpClient will have the access token in it’s request headers. In the sections below we will see a few ways of getting this access token.
Server app registration
An app registration is associated with the server function app. This app registration is server’s identity and is used to secure the server function app.
Application role
The server app registration is setup with an application role let’s say “Orders.Read” — which is a custom role. (This role is exposed as an application permission that can be added in other app registrations.)
To make sure that incoming requests have role assignments and only apps with “Orders.Read” permission can get the data from the server, the code in the server function app verifies that the incoming requests have “Orders.Read” role in the request. This is done by checking the “roles” claim of the incoming request.
req.HttpContext.ValidateAppRole(new string[] { “Orders.Read” });
Client function app
The client function app is a daemon app which uses it’s own identity (client app registration) to get data from the server function app. (The client function app can also be a non-daemon app but will still use it’s own identity (not users identity) to get data from server).
The client app registration will have “Orders.Read” application permission that will be approved by the admin. With this, the access token that gets generated in the client function app’s code will have “Orders.Read” role in the roles claim.
In the following sections we will see how to generate the access token in the client function app.
Code
All the code explained below can be found in this GitHub repository. Please note that the code is to show the concepts only and is not PROD ready.
Generating access token
Microsoft provide us with various authentication libraries which do all the hard work of computing the access token and checking it’s expiry. So, all we need to do is install one of those libraries in our project and use the methods that are provided by these libraries and get an access token. Let’s take a look at those libraries and the methods they provide:
1. ADAL (not recommended / deprecated)
Note: Using ADAL is deprecated hence not recommended. So use MSAL instead.
Using ADAL we create an AuthenticationContext
object and get the access token using AcquireTokenAsync
method of the authentication context object as shown in the image below and the code here.
2. MSAL.NET
In MSAL.NET, apps are represented either as public client or confidential client applications. In our case the client app registration will be represented as confidential client application. So, we create a variable of type IConfidentialClientApplication
using the ConfidentialClientApplicationBuilder
class provided by MSAL. We need to provide the client app registration’s ID, it’s secret (or certificate in case of PROD) and the tenant ID.
We then use the AcquireTokenForClient
method with the scopes as “ServerAppRegId/.default” to get an access token. The access token will have the server app registration’s ID in the aud
claim and will have the Orders.Read
entry in the roles
claim.
The code for the same can be seen in the image below and here.
Managed identities
Instead of using the client app registration to represent the client function app, we can create an identity in a different way for that function app by using managed identity. This will help us move away from maintaining client secret or certificate.
Managed identity for the client function app can be created by following the information in this Microsoft article.
The managed identity that is created will not have any permissions to talk to the server app. Hence we need to add “Orders.Read” application permission to the managed identity. We can do that either using Azure PowerShell or Microsoft 365 CLI. The Microsoft 365 command will be:
m365 aad approleassignment add — displayName “client-function-app” — resource “server-app-reg-name” — scope “Orders.Read”
Once the permission is assigned we can create a request to get an access token, to access the server app, using the managed identity of the client function app. Let’s see a couple of ways in which we can do that.
3. App Authentication client library for .NET
Note: For new applications Microsoft recommend using Azure.Identity instead of this library.
With this library we create an instance of AzureServiceTokenProvider
with the connection string as RunAs=App
. We then use the GetAccessTokenAsync
method by passing in the server app registration’s ID to get an access token.
The access token will have the server app registration’s ID in the aud
claim and will have the Orders.Read
entry in the roles
claim.
The code for the same can be seen in the image below and here.
4. Azure.Identity library
Microsoft have mentioned in this article that “Azure.Identity can be considered as the spiritual successor of the App Authentication client library for .NET”. Using Azure.Identity library we create an instance of ManagedIdentityCredential
class. This will automatically use the credentials of the managed identity configured for the client function app. We then call the GetTokenAsync
method with the scopes as “ServerAppRegId/.default” to get an access token. The access token will have the server app registration’s ID in the aud
claim and will have the Orders.Read
entry in the roles
claim.
The code for the same can be seen in the image below and here.
Local development auth
During development if we want to test the above functionality (3 and 4) then, we have cannot use Managed Identity for authentication locally. Hence we have to use different methods of authentication.
More information on those methods can be found here and here.
Videos related to the same by Garry Trinder and Rahul Nath in their blog articles.
In the code that is shared along with this blog, we will be using the credentials of the client app registration for authentication during local development. This can be seen here and here. We make use of client app registration’s ID and secret to authenticate. This needs to be the case only in DEV tenant. In PROD there will be no need to have client app registration when using Managed Identity.
Further enhancements
The code in the server function app at the moment checks only if the token has the correct roles
claim. The code can be enhanced further to perform more checks based on the security requirement. Those checks might be verifying if the aud
claim in the request is the intended one or not etc.
Summary
We have seen 4 different ways of getting an access token for a daemon app (Client) to call an API (Server)using the different libraries provided by Microsoft. Thanks to Microsoft, all the hard work of computing the access token is done by the libraries. We have seen how easy it is to use the methods provided by those libraries.
If we are using an app registration to represent the client function app then we should use MSAL to get the access token and if we are using managed identity to represent the client function app then for the new development it is recommended to use Azure.Identity to get the access token.