A web app that calls web APIs: Code configuration

As shown in the Web app that signs in users scenario, the web app uses the OAuth 2.0 authorization code flow to sign the user in. This flow has two steps:

  1. Request an authorization code. This part delegates a private dialogue with the user to the Microsoft identity platform. During that dialogue, the user signs in and consents to the use of web APIs. When the private dialogue ends successfully, the web app receives an authorization code on its redirect URI.
  2. Request an access token for the API by redeeming the authorization code.

The Web app that signs in users scenarios covered only the first step. Here you learn how to modify your web app so that it not only signs users in but also now calls web APIs.

Microsoft libraries supporting web apps

The following Microsoft libraries support web apps:

Language / framework Project on
GitHub
Package Getting
started
Sign in users Access web APIs Generally available (GA) or
Public preview1
.NET MSAL.NET Microsoft.Identity.Client Library cannot request ID tokens for user sign-in. Library can request access tokens for protected web APIs. GA
.NET Microsoft.IdentityModel Microsoft.IdentityModel Library cannot request ID tokens for user sign-in.2 Library cannot request access tokens for protected web APIs.2 GA
ASP.NET Core ASP.NET Security Microsoft.AspNetCore.Authentication Quickstart Library can request ID tokens for user sign-in. Library cannot request access tokens for protected web APIs. GA
ASP.NET Core Microsoft.Identity.Web Microsoft.Identity.Web Quickstart Library can request ID tokens for user sign-in. Library can request access tokens for protected web APIs. GA
Java MSAL4J msal4j Quickstart Library can request ID tokens for user sign-in. Library can request access tokens for protected web APIs. GA
Node.js MSAL Node msal-node Quickstart Library can request ID tokens for user sign-in. Library can request access tokens for protected web APIs. GA
Python MSAL Python msal Quickstart Library can request ID tokens for user sign-in. Library can request access tokens for protected web APIs. GA

1 Supplemental terms of use for Microsoft Azure Previews apply to libraries in Public preview.

2 The Microsoft.IdentityModel library only validates tokens - it cannot request ID or access tokens.

Select the tab for the platform you're interested in:

Client secrets or client certificates

Given that your web app now calls a downstream web API, provide a client secret or client certificate in the appsettings.json file. You can also add a section that specifies:

  • The URL of the downstream web API
  • The scopes required for calling the API

In the following example, the GraphBeta section specifies these settings.

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "ClientId": "[Client_id-of-web-app-eg-2ec40e65-ba09-4853-bcde-bcb60029e596]",
    "TenantId": "common"

   // To call an API
   "ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
   "ClientCertificates": [
  ]
 },
 "GraphBeta": {
    "BaseUrl": "https://graph.microsoft.com/beta",
    "Scopes": "user.read"
    }
}

Instead of a client secret, you can provide a client certificate. The following code snippet shows using a certificate stored in Azure Key Vault.

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "ClientId": "[Client_id-of-web-app-eg-2ec40e65-ba09-4853-bcde-bcb60029e596]",
    "TenantId": "common"

   // To call an API
   "ClientCertificates": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://msidentitywebsamples.vault.azure.net",
        "KeyVaultCertificateName": "MicrosoftIdentitySamplesCert"
      }
   ]
  },
  "GraphBeta": {
    "BaseUrl": "https://graph.microsoft.com/beta",
    "Scopes": "user.read"
  }
}

Microsoft.Identity.Web provides several ways to describe certificates, both by configuration or by code. For details, see Microsoft.Identity.Web - Using certificates on GitHub.

Startup.cs

Your web app will need to acquire a token for the downstream API. You specify it by adding the .EnableTokenAcquisitionToCallDownstreamApi() line after .AddMicrosoftIdentityWebApi(Configuration). This line exposes the ITokenAcquisition service that you can use in your controller and page actions. However, as you'll see in the following two options, it can be done more simply. You'll also need to choose a token cache implementation, for example .AddInMemoryTokenCaches(), in Startup.cs:

using Microsoft.Identity.Web;

public class Startup
{
  // ...
  public void ConfigureServices(IServiceCollection services)
  {
  // ...
  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
          .AddMicrosoftIdentityWebApp(Configuration, Configuration.GetSection("AzureAd"))
            .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"user.read" })
            .AddInMemoryTokenCaches();
   // ...
  }
  // ...
}

The scopes passed to EnableTokenAcquisitionToCallDownstreamApi are optional, and enable your web app to request the scopes and the user's consent to those scopes when they log in. If you don't specify the scopes, Microsoft.Identity.Web will enable an incremental consent experience.

If you don't want to acquire the token yourself, Microsoft.Identity.Web provides two mechanisms for calling a web API from a web app. The option you choose depends on whether you want to call Microsoft Graph or another API.

Option 1: Call Microsoft Graph

If you want to call Microsoft Graph, Microsoft.Identity.Web enables you to directly use the GraphServiceClient (exposed by the Microsoft Graph SDK) in your API actions. To expose Microsoft Graph:

  1. Add the Microsoft.Identity.Web.MicrosoftGraph NuGet package to your project.

  2. Add .AddMicrosoftGraph() after .EnableTokenAcquisitionToCallDownstreamApi() in the Startup.cs file. .AddMicrosoftGraph() has several overrides. Using the override that takes a configuration section as a parameter, the code becomes:

    using Microsoft.Identity.Web;
    
    public class Startup
    {
      // ...
      public void ConfigureServices(IServiceCollection services)
      {
      // ...
      services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
              .AddMicrosoftIdentityWebApp(Configuration, Configuration.GetSection("AzureAd"))
                .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"user.read" })
                   .AddMicrosoftGraph(Configuration.GetSection("GraphBeta"))
                .AddInMemoryTokenCaches();
       // ...
      }
      // ...
    }
    

Option 2: Call a downstream web API other than Microsoft Graph

To call a web API other than Microsoft Graph, Microsoft.Identity.Web provides .AddDownstreamWebApi(), which requests tokens and calls the downstream web API.

using Microsoft.Identity.Web;

public class Startup
{
  // ...
  public void ConfigureServices(IServiceCollection services)
  {
  // ...
  services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
          .AddMicrosoftIdentityWebApp(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi(new string[]{"user.read" })
               .AddDownstreamWebApi("MyApi", Configuration.GetSection("GraphBeta"))
            .AddInMemoryTokenCaches();
   // ...
  }
  // ...
}

Summary

As with web APIs, you can choose various token cache implementations. For details, see Microsoft.Identity.Web - Token cache serialization on GitHub.

The following image shows the various possibilities of Microsoft.Identity.Web and their impact on the Startup.cs file:

Block diagram showing service configuration options in startup dot C S for calling a web API and specifying a token cache implementation

Note

To fully understand the code examples here, be familiar with ASP.NET Core fundamentals, and in particular with dependency injection and options.

Code that redeems the authorization code

Microsoft.Identity.Web simplifies your code by setting the correct OpenID Connect settings, subscribing to the code received event, and redeeming the code. No extra code is required to redeem the authorization code. See Microsoft.Identity.Web source code for details on how this works.

Instead of a client secret, the confidential client application can also prove its identity by using a client certificate, or a client assertion. The use of client assertions is an advanced scenario, detailed in Client assertions.

Token cache

Important

The token-cache implementation for web apps or web APIs is different from the implementation for desktop applications, which is often file based. For security and performance reasons, it's important to ensure that for web apps and web APIs there is one token cache per user account. You must serialize the token cache for each account.

The ASP.NET core tutorial uses dependency injection to let you decide the token cache implementation in the Startup.cs file for your application. Microsoft.Identity.Web comes with pre-built token-cache serializers described in Token cache serialization. An interesting possibility is to choose ASP.NET Core distributed memory caches:

// Use a distributed token cache by adding:
    services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi(
                initialScopes: new string[] { "user.read" })
            .AddDistributedTokenCaches();

// Then, choose your implementation.
// For instance, the distributed in-memory cache (not cleared when you stop the app):
services.AddDistributedMemoryCache();

// Or a Redis cache:
services.AddStackExchangeRedisCache(options =>
{
 options.Configuration = "localhost";
 options.InstanceName = "SampleInstance";
});

// Or even a SQL Server token cache:
services.AddDistributedSqlServerCache(options =>
{
 options.ConnectionString = _config["DistCache_ConnectionString"];
 options.SchemaName = "dbo";
 options.TableName = "TestCache";
});

For details about the token-cache providers, see also Microsoft.Identity.Web's Token cache serialization article, and the ASP.NET Core Web app tutorials | Token caches phase of the web apps tutorial.

Next steps

At this point, when the user signs in, a token is stored in the token cache. Let's see how it's then used in other parts of the web app.

Remove accounts from the cache on global sign-out