Calling Web API to Web API without User Interaction by using Azure Managed Identity

Introduction

In Azure, there are two commonly used ways to authenticate and authorize Web APIs to access other Web APIs without user interaction.

  • Managed Identity(Admin consent required)
  • Client Credentials Flow (Admin consent required) - This will be explained in the next post.

Azure Managed Identity

Azure Managed Identity provides an automatic and seamless way to authenticate applications or services within Azure. With Managed Identity, Azure takes care of managing the credentials and eliminates the need to store secrets or manage client credentials manually. When a Web API with Managed Identity needs to call another Web API, it can use its Managed Identity to authenticate and authorize the request without requiring explicit credentials.

To configure and execute the service Tech stack and Azure services that you need

  • Azure Active Directory service
    • Create App (SPN) registration
  • Azure Web App service - 2 (You must deploy both the Web APIs to work. It will not work locally/on your machine)
  • Admin consent is required on Azure PowerShell to execute the script
  • 2 .NET Core Web API projects.

Follow these steps

  1. Create two different Azure App services, WebAPI1 and WebAPI2, in the Azure portal.
  2. Go to WebAPI1, select Identity under the Settings section, and ON the status of System Assigned. Copy the Object (Principal) ID.
    Calling Web API to Web API without User Interaction by using Azure Managed Identity
  3. To create App registration, follow these steps -Navigate to the Azure Active Directory service.
    • Under the "Manage" section, select "App registrations."
    • Click on the "New registration" button.
    • Provide a meaningful name for the app registration, such as "WebAPI-mi".
    • Choose "Accounts in this organizational directory only" (Default Directory only - Single tenant) as the account type.
    • Click the "Register" button to create the app registration.
  4. Create an App role in App Registration.
    • Go to App Roles under the Manage section, click on create app roles and provide the display name and value as "app_role_access_mi" and description. Make sure to enable the state.Calling Web API to Web API without User Interaction by using Azure Managed Identity
  5. Execute the below PowerShell script either in the Azure portal of Azure CLI or through Windows PowerShell by granting Admin consent and installing the modules given in the comments.
    • If you want to add the app role for Microsoft.Graph then executes the script given in the above link.
    • If you want to add the custom app role for your WebAPI2, then execute the below script using PowerShell.
      # Install the module.
      # Install-Module Microsoft.Graph -Scope CurrentUser
      
      # The tenant ID - replace from Azure active directory overview page
      $TenantId = "111111111111111111111111111111111"  
      
      # The name of your web API name and the resource group name, which has a managed identity.
      $webAppName = "WebAPI1" 
      # Resource group of WebAPIs
      $resourceGroupName = "WebAPI-RG"   
      
      # The name of the app role that the managed identity should be assigned to.
      $appRoleName = "app_role_access_mi"
      
      # Get the web app's managed identity's object ID.
      Connect-AzAccount -Tenant $TenantId
      $managedIdentityObjectId = (Get-AzWebApp -ResourceGroupName $resourceGroupName -Name $webAppName).identity.principalid
      
      # Get Microsoft Graph app's service principal and app role.
      $serverApplicationName = "WebAPI2"
      $serverServicePrincipal = (Get-MgServicePrincipal -Filter "DisplayName eq '$serverApplicationName'")
      $serverServicePrincipalObjectId = $serverServicePrincipal.Id
      
      $appRoleId = ($serverServicePrincipal.AppRoles | Where-Object {$_.Value -eq $appRoleName }).Id
      
      # Assign the managed identity access to the app role.
      New-MgServicePrincipalAppRoleAssignment `
          -ServicePrincipalId $managedIdentityObjectId `
          -PrincipalId $managedIdentityObjectId `
          -ResourceId $serverServicePrincipalObjectId `
          -AppRoleId $appRoleId
      
  6. Once the above script is executed, make sure to verify that you have registered in the AAD. Go to the enterprise application of Azure AD and search Object (Principal) ID from step 2. Select the enterprise app and go to security Permissions and verify.
    Calling Web API to Web API without User Interaction by using Azure Managed Identity

  7. In WebAPI1, using  Asp.NET Core 6.0, copy and paste the below snippet in your method to call the WebAPI2.Make sure to copy and paste the ClientID of WebAPI-mi app registration from Step 3.
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Identity.Web;
    using Azure.Core;
    using Azure.Identity;
    using System.Net.Http.Headers;
    
    class YourController
    {
        [HttpGet]
        public async Task<ActionResult> CallWebAPI2()
        {
            // Create an instance of DefaultAzureCredential using the client ID or object ID of your Web API's managed identity.
            var credential = new DefaultAzureCredential();
    
            // Create an instance of HttpClient.
            var httpClient = new HttpClient();
    
            // Authenticate the HttpClient instance using the credential.
            // Copy and paste the Application (Client ID) of WebAPI-mi app registration
            var accessToken = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(new[] { "api://XXXXXXXX-0000-0000-0000-XXXXXXXXXXXX/.default" }));
            httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken.Token);
    
            // Make requests to the target Web API.
            // The actual request to the secured "integration" API
            var result = await httpClient.GetStringAsync("https://WebAPI2.azurewebsites.net/WeatherForecast");
    
            return Ok($"Response: {result}" + "Token :" + accessToken.Token);
        }
    }
    
  8. In WebAPI2, copy and paste the below code in both the AppSettings.Json and Program.cs files.

    Appsettings.json 
    
    {  
      "AzureAd" : { 
        "Instance" :  "https://login.microsoftonline.com/"
        "Domain" : "yourdomain.onmicrosoft.com"  //It comes from an overview page of Azure Active Directory
        "TenantId" : "XXXXXXXX-0000-0000-0000-XXXXXXXXXXXX"  //It comes from an overview page of Azure Active Directory
        "ClientId" :  "XXXXXXXX-0000-0000-0000-XXXXXXXXXXXX"  //Copy and paste the Application (Client ID) of WebAPI-mi app registration
        },
      "Logging": {
            "LogLevel": {
                "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
            }
        },
      "AllowedHosts": "*"
    }
    //Program.cs file
    
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Identity.Web;
    
    var builder = WebApplication.CreateBuilder(args);
    
     // Your existing code here
            
     // Add authentication services
     builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
            
     // Your existing code here
            
     // Configure the HTTP request pipeline
     app.UseAuthentication();
     app.UseAuthorization();
            
     // Your existing code here
            
     // Start the application
     app.Run();
    
    
    
    // Create the controller with the name WeatherForecastController.cs and the below snippet. Add the [Authorize] attribute for authorization
    
    [Authorize]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
    
        private readonly ILogger<WeatherForecastController> _logger;
    
        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }
    
        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
    
    // Add this code in the WeatherForeCast.cs file
    public class WeatherForecast
    {
        public DateTime Date { get; set; }
        public int TemperatureC { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
        public string? Summary { get; set; }
    }

    Deploy each solution in the App Service of WebAPI1 and WebAPI2. Execute the CallWebAPI2method in WebAPI1, and here is the response.
    Calling Web API to Web API without User Interaction by using Azure Managed Identity

  9. Copy and paste the token in jwt to deserialize the token, and you will see the App role assigned to the token to authorize in WebAPI2.
    Calling Web API to Web API without User Interaction by using Azure Managed Identity

This is how you can authenticate and authorize using API to API by using the Managed identity. Stay tuned for the next post on Client credentials flow.