Azure Functions - mattchenderson/microsoft-identity-web GitHub Wiki

The Microsoft Identity Web library enables Azure Functions to work with the Microsoft identity platform, enabling them to process access tokens for both work and school and Microsoft personal accounts, as well as Azure AD B2C.

Why use Microsoft.Identity.Web with Azure Functions?

From the point of view of Microsoft.Identity.Web, Azure Functions with HTTP trigger are very similar to web APIs.

This library adds ServiceCollection and AuthenticationBuilder extension methods for use in the ASP.NET Core web app Startup.cs file. These extension methods enable the web app to sign in users with the Microsoft identity platform and, optionally, enable the web app to call APIs on behalf of the signed-in user.

Using the func2 project template

It also adds a project template to create an Azure Functions application:

  • dotnet new func2 --auth SingleOrg for AAD protected services
  • dotnet new func2 --auth IndividualB2C for Azure AD B2C protected services

If you use these project templates, you'll get a fully functional application once you have filled in the configuration.

In the case of AAD protected services, you can also create an Azure Function that calls Microsoft Graph or a downstream API.

  • dotnet new func2 --auth SingleOrg --calls-graph
  • dotnet new func2 --auth SingleOrg --called-api-url URL --called-api-scopes SCOPES

While the generated code demonstrates the usage of Microsoft.Identity.Web to get a token on the user's behalf using the on-behalf of flow, the code can be easily customized to support other scenarios Azure Functions and Microsoft.Identity.Web support. For instance, asking for an application permission token in a timer triggered Azure function. Additionally, getting a token on user's behalf is not possible with Azure AD B2C, as the service does not allow the on-behalf of flow (web APIs calling downstream APIs).

If you want to add authentication to an existing Azure Functions app, the following paragraph explain how to modify the code for your application.

Azure Functions - Startup.cs

Create an identityConfig.json file with the following structure describing your Microsoft Identity Platform application

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "msidentitysamplestesting.onmicrosoft.com",
    "TenantId": "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab",
    "ClientId": "a4c2469b-cf84-4145-8f5f-cb7bacf814bc"
  }
}

To enable the Azure Function to be protected with the Microsoft identity platform:

  1. Add the Microsoft.Identity.Web NuGet package

  2. If you don't have one already, create a Startup.cs file for dependency injection in Azure Functions.

  3. Update the configuration to use the file you defined earlier. This goes in the ConfigureAppConfiguration() method.

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        FunctionsHostBuilderContext context = builder.GetContext();
        builder.ConfigurationBuilder
           .AddJsonFile(Path.Combine(context.ApplicationRootPath, "identityConfig.json"), optional: true, reloadOnChange: false)
           .AddEnvironmentVariables();
    }
    
  4. In the Configure() method, register the authentication and authorization services:

    var aadSection = builder.GetContext().Configuration.GetSection("AzureAd");
    builder.Services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = Microsoft.Identity.Web.Constants.Bearer;
        sharedOptions.DefaultChallengeScheme = Microsoft.Identity.Web.Constants.Bearer;
    })
    .AddMicrosoftIdentityWebApi(builder.GetContext().Configuration);
    
    builder.Services.AddAuthorization(options =>
    {
        options.FallbackPolicy = options.DefaultPolicy;
    });
    

Case of a B2C Web API

Assuming you have a similar configuration in identityConfig.json:

{
  "AzureAdB2C": {
    "Instance": "https://fabrikamb2c.b2clogin.com",
    "ClientId": "90c0fe63-bcf2-44d5-8fb7-b8bbc0b29dc6",
    "Domain": "fabrikamb2c.onmicrosoft.com",
    "SignedOutCallbackPath": "/signout/B2C_1_susi",
    "SignUpSignInPolicyId": "b2c_1_susi",
    "ResetPasswordPolicyId": "b2c_1_reset",
    "EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy
  }
}

To enable the web API to accept tokens emitted by Azure AD B2C, modify the authentication service registration to the following:

builder.Services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = Microsoft.Identity.Web.Constants.Bearer;
sharedOptions.DefaultChallengeScheme = Microsoft.Identity.Web.Constants.Bearer;
})
.AddMicrosoftWebApi(Configuration, "AzureAdB2C");

Verification of scopes or app roles

Your functions should verify that the caller has the right permissions in the form of:

  • scopes, in the case of APIs called on behalf of a user
  • app roles, in the case of APIs called by daemon applications

Verify scopes in web APIs called on behalf of users

An function that is called on behalf of users needs to verify the scopes using the VerifyUserHasAnyAcceptedScope() extension method on the HttpContext.

[FunctionName("SampleFunc")]
public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    ILogger log)
{
    var (authenticationStatus, authenticationResponse) = await req.HttpContext.AuthenticateAzureFunctionAsync();
    if (!authenticationStatus) {
        return authenticationResponse;
    }
    req.HttpContext.VerifyUserHasAnyAcceptedScope(new string[] { "access_as_user" });

    using var response = await _downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false);

    // ...
}

Azure Functions called by daemon apps (using client credential flow)

An app that accepts daemon applications needs to:

To support ACL-based authorization

If you want to enable the ACL-based authorization, you'll need to set the AllowWebApiToBeAuthorizedByACL to true in the configuration. otherwise, Microsoft.Identity.Web will no longer throw an exception when neither roles or scopes are not in the Claims provided. If you set this property to true in the identityConfig.json or programmatically, this is your responsibility to ensure the ACL mechanism.

{
 "AzureAD":
 { 
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}