Skip to content

Latest commit

 

History

History
 
 
services platforms author level client service endpoint
active-directory
dotnet
jmprieur
200
ASP.NET Core Web App
Microsoft Graph
Microsoft identity platform

ASP.NET Core 2.2 Web API calling ARM and Azure Storage

Build status

Scenario

This sample shows how to update your ASP.NET Core Web API so that it now calls other Microsoft APIs than Microsoft Graph. The sample now calls the Azure Resource Manager API as well as the Azure Storage

Sign in and call ARM and Azure Storage

How to run this sample

To run this sample:

Pre-requisites:

This is the third phase of the tutorial. It's recommended that you have gone through the previous phases of the tutorial, in particular how the WebApp signs-in users with Microsoft Identity (OIDC) / with work and school or personal accounts and Web app calls the Microsoft Graph API on behalf of a user signing-in.

This chapter shows the incremental changes required to call two Microsoft APIs other than Microsoft Graph (Azure Resource Management and Azure Storage).

Step 1: Register the sample with your Azure AD tenant

You first need to register your app as described in the first phase of the tutorial

Then, the follow the following extra set of steps:

  1. In the API permissions section for the application, notice that a delegated permission is set by default to Microsoft Graph for the scope User.Read
  2. Select Add a permission
    • In the Microsoft APIs tab, select Azure Service Management
    • Check user_impersonation
    • Select Add permissions
  3. Select Add a permission
    • In the Microsoft APIs tab, select Azure Storage. If you cannot find it in this category, try in the APIs my organization uses tab
    • Check user_impersonation
    • Select Add permissions

For the Azure Storage preparation see Authenticate with Azure Active Directory from an application for access to blobs and queues

Step 2: Download/Clone/Go to the folder containing the sample code and build the application

If you have not already, clone this sample from your shell or command line:

git clone https://github.com/Azure-Samples/microsoft-identity-platform-aspnetcore-webapp-tutorial webapp
cd webapp
cd "3-WebApp-multi-APIs"

In the appsettings.json file, replace, if you have not already:

  • the ClientID value with the Application ID from the application you registered in Application Registration portal,
  • the TenantId by common, as here you chose to sign-in users with their work or school or personal account. In case you want to sign-in different audiences, refer back to the first phase of the tutorial
  • and the ClientSecret by the client secret you generated in Step 1.

Step 3: Run the sample

  1. Build the solution and run it.

  2. Open your web browser and make a request to the app. The app immediately attempts to authenticate you via the Microsoft identity platform endpoint. Sign in with your personal account or with a work or school account.

  3. Go to the Contacts page, you should now see all kind of information about yourself (a call was made to the Microsoft Graph me endpoint)

About The code

Starting from the previous phase of the tutorial, the code was incrementally updated with the following steps:

Update the Startup.cs file to enable TokenAcquisition by a MSAL.NET based service

After the following lines in the ConfigureServices(IServiceCollection services) method, after services.AddMicrosoftIdentityPlatformAuthentication(Configuration);, add services.AddHttpClient<IArmOperations, ArmApiOperationService>();:

 public void ConfigureServices(IServiceCollection services)
{
    . . .
    // Token acquisition service based on MSAL.NET 
    // and chosen token cache implementation
    services.AddMicrosoftIdentityPlatformAuthentication(Configuration)
            .AddMsal(new string[] { Constants.ScopeUserRead })
            .AddInMemoryTokenCache();
    services.AddHttpClient<IArmOperations, ArmApiOperationService>();

This enables to add the Azure Resource manager micro-service to use the HttpClient by dependency injection.

Add the Services\ARM sub folder

The Services\ARM sub folder which is a simple wrapper against the ARM REST API.

Add the Tenants.cshtml view

In the Views\Home folder add a view named Tenants.cshtml

@using System
@using System.Collections.Generic
@{
    ViewData["Title"] = "Tenants";
    IDictionary<string, string> tenants = ViewData["tenants"] as IDictionary<string, string>;
    if (tenants == null)
    {
        tenants = (ViewData["tenants"] as IEnumerable<string>).ToDictionary(name => name);
    }
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

<table class="table table-striped table-condensed" style="font-family: monospace">
    <tr>
        <th>Tenant ID</th>
        <th>Tenant name</th>
    </tr>

    @foreach(var tenant in tenants)
    {
    <tr>
        <td>@tenant.Key</td>
        <td>@tenant.Value</td>
    </tr>
    }
</table>

Add methods in the HomeController to call ARM, and Azure storage

  // Requires that the app has added the Azure Service Management / user_impersonation scope, and that
  // the admin tenant does not require admin consent for ARM.
  [AuthorizeForScopes(Scopes = new[] { "https://management.azure.com/user_impersonation"})]
  public async Task<IActionResult> Tenants()
  {
      var accessToken =
          await tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext,
             new[] { $"{ArmApiOperationService.ArmResource}user_impersonation" });

      var tenantIds = await armOperations.EnumerateTenantsIdsAccessibleByUser(accessToken);
      ViewData["tenants"] = tenantIds;

      return View();
  }


  [AuthorizeForScopes(Scopes = new[] { "https://storage.azure.com/user_impersonation" })]
  public async Task<IActionResult> Blob()
  {
      var scopes = new string[] { "https://storage.azure.com/user_impersonation" };

      var accessToken =
          await tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext, scopes);

      // create a blob on behalf of the user
      TokenCredential tokenCredential = new TokenCredential(accessToken);
      StorageCredentials storageCredentials = new StorageCredentials(tokenCredential);

      // replace the URL below with your storage account URL
      Uri blobUri = new Uri("https://blobstorageazuread.blob.core.windows.net/sample-container/Blob1.txt");
      CloudBlockBlob blob = new CloudBlockBlob(blobUri, storageCredentials);
      await blob.UploadTextAsync("Blob created by Azure AD authenticated user.");

      ViewData["Message"] = "Blob successfully created";
      return View();
  }

Add new buttons in the menu bar to call the new actions

In the Views\Shared\_Layout.cshtml file

after:

 <li><a asp-area="" asp-controller="Home" asp-action="Profile">Profile</a></li>

insert

  <li><a asp-area="" asp-controller="Home" asp-action="Tenants">Tenants</a></li>
  <li><a asp-area="" asp-controller="Home" asp-action="Blob">Blob</a></li>

Troubleshooting

To access Azure Resource Management (ARM), you'll need a work or school account (AAD account) and an Azure subscription. If your Azure subscription is for a Microsoft personal account, just create a new user in your directory, and use this user to run the sample

OpenIdConnectProtocolException: Message contains error: 'invalid_client', error_description: 'AADSTS650052: The app needs access to a service ("https://*.blob.core.windows.net") that your organization "tenantname.onmicrosoft.com" has not subscribed to or enabled. Contact your IT Admin to review the configuration of your service subscriptions. this is because the AzureStorage API was not registered as an API used by your Web App

Learn more

You can learn more about the tokens by looking at the following articles in MSAL.NET's conceptual documentation: