aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md
This article explains how to create a hosted Blazor WebAssembly solution that uses Azure Active Directory (AAD) B2C for authentication.
For additional security scenario coverage after reading this article, see xref:blazor/security/webassembly/additional-scenarios.
The subsections of the walkthrough explain how to:
Follow the guidance in Tutorial: Create an Azure Active Directory B2C tenant to create an AAD B2C tenant.
Before proceeding with this article's guidance, confirm that you've selected the correct directory for the AAD B2C tenant.
Register an AAD B2C app for the Server API app:
Record the following information:
00001111-aaaa-2222-bbbb-3333cccc4444)https://contoso.b2clogin.com/, which includes the trailing slash). The instance is the scheme and host of an Azure B2C app registration, which can be found by opening the Endpoints window from the App registrations page in the Azure portal.contoso.onmicrosoft.com): The domain is available as the Publisher domain in the Branding blade of the Azure portal for the registered app.Select Expose an API from the sidebar and follow these steps:
API.Access).Access API).Allows the app to access server app API endpoints.).Record the following information:
00001111-aaaa-2222-bbbb-3333cccc4444 from https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444)API.Access)Register an AAD B2C app for the Client app:
https://localhost/authentication/login-callback. If you know the production redirect URI for the Azure default host (for example, azurewebsites.net) or the custom domain host (for example, contoso.com), you can also add the production redirect URI at the same time that you're providing the localhost redirect URI. Be sure to include the port number for non-:443 ports in any production redirect URIs that you add.[!NOTE] Supplying the port number for a
localhostAAD B2C redirect URI isn't required. For more information, see Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Entra documentation).
Record the Client app Application (client) ID (for example, 11112222-bbbb-3333-cccc-4444dddd5555).
In Authentication > Platform configurations > Single-page application:
https://localhost/authentication/login-callback is present.In API permissions from the sidebar:
API.Access) with the checkbox.Return to Azure AD B2C in the Azure portal. Select User flows and use the following guidance: Create a sign-up and sign-in user flow. At a minimum, select Application claims for the sign-up/sign-in user flow and then the Display Name user attribute checkbox to populate the context.User.Identity?.Name/context.User.Identity.Name in the LoginDisplay component (Shared/LoginDisplay.razor).
Record the sign-up and sign-in user flow name created for the app (for example, B2C_1_signupsignin1).
Replace the placeholders in the following command with the information recorded earlier and execute the command in a command shell:
dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --api-client-id "{SERVER API APP CLIENT ID}" --app-id-uri "{SERVER API APP ID URI GUID}" --client-id "{CLIENT APP CLIENT ID}" --default-scope "{DEFAULT SCOPE}" --domain "{TENANT DOMAIN}" -ho -o {PROJECT NAME} -ssp "{SIGN UP OR SIGN IN POLICY}"
[!WARNING] Avoid using dashes (
-) in the app name{PROJECT NAME}that break the formation of the OIDC app identifier. Logic in the Blazor WebAssembly project template uses the project name for an OIDC app identifier in the solution's configuration. Pascal case (BlazorSample) or underscores (Blazor_Sample) are acceptable alternatives. For more information, see Dashes in a hosted Blazor WebAssembly project name break OIDC security (dotnet/aspnetcore #35337).
| Placeholder | Azure portal name | Example |
|---|---|---|
{AAD B2C INSTANCE} | Instance | https://contoso.b2clogin.com/ (includes the trailing slash) |
{PROJECT NAME} | — | BlazorSample |
{CLIENT APP CLIENT ID} | Application (client) ID for the :::no-loc text="Client"::: app | 11112222-bbbb-3333-cccc-4444dddd5555 |
{DEFAULT SCOPE} | Scope name | API.Access |
{SERVER API APP CLIENT ID} | Application (client) ID for the :::no-loc text="Server"::: app | 00001111-aaaa-2222-bbbb-3333cccc4444 |
{SERVER API APP ID URI GUID} | Application ID URI GUID | 00001111-aaaa-2222-bbbb-3333cccc4444 (GUID ONLY, matches the {SERVER API APP CLIENT ID}) |
{SIGN UP OR SIGN IN POLICY} | Sign-up/sign-in user flow | B2C_1_signupsignin1 |
{TENANT DOMAIN} | Primary/Publisher/Tenant domain | contoso.onmicrosoft.com |
The output location specified with the -o|--output option creates a project folder if it doesn't exist and becomes part of the project's name. Avoid using dashes (-) in the app name that break the formation of the OIDC app identifier (see the earlier WARNING).
User.Identity.NameThe guidance in this section covers optionally populating User.Identity.Name with the value from the name claim.
The :::no-loc text="Server"::: app API populates User.Identity.Name with the value from the http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name claim type (for example, [email protected]).
To configure the app to receive the value from the name claim type:
Add a namespace for xref:Microsoft.AspNetCore.Authentication.JwtBearer?displayProperty=fullName to the Program file:
using Microsoft.AspNetCore.Authentication.JwtBearer;
Configure the xref:Microsoft.IdentityModel.Tokens.TokenValidationParameters.NameClaimType?displayProperty=nameWithType of the xref:Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions in the Program file:
builder.Services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.NameClaimType = "name";
});
This section describes the parts of a solution generated from the Blazor WebAssembly project template and describes how the solution's :::no-loc text="Client"::: and :::no-loc text="Server"::: projects are configured for reference. There's no specific guidance to follow in this section for a basic working application if you created the app using the guidance in the Walkthrough section. The guidance in this section is helpful for updating an app to authenticate and authorize users. However, an alternative approach to updating an app is to create a new app from the guidance in the Walkthrough section and moving the app's components, classes, and resources to the new app.
appsettings.json configurationThis section pertains to the solution's :::no-loc text="Server"::: app.
The appsettings.json file contains the options to configure the JWT bearer handler used to validate access tokens:
{
"AzureAdB2C": {
"Instance": "https://{TENANT}.b2clogin.com/",
"ClientId": "{SERVER API APP CLIENT ID}",
"Domain": "{TENANT DOMAIN}",
"Scopes": "{DEFAULT SCOPE}",
"SignUpSignInPolicyId": "{SIGN UP OR SIGN IN POLICY}"
}
}
Example:
{
"AzureAdB2C": {
"Instance": "https://contoso.b2clogin.com/",
"ClientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
"Domain": "contoso.onmicrosoft.com",
"Scopes": "API.Access",
"SignUpSignInPolicyId": "B2C_1_signupsignin1",
}
}
This section pertains to the solution's :::no-loc text="Server"::: app.
The support for authenticating and authorizing calls to ASP.NET Core web APIs with the Microsoft identity platform is provided by the Microsoft.Identity.Web package.
The :::no-loc text="Server"::: app of a hosted Blazor solution created from the Blazor WebAssembly template includes the Microsoft.Identity.Web.UI package. The package adds UI for user authentication in web apps and isn't used by the Blazor framework. If the :::no-loc text="Server"::: app won't be used to authenticate users directly, it's safe to remove the package reference from the :::no-loc text="Server"::: app's project file.
This section pertains to the solution's :::no-loc text="Server"::: app.
The AddAuthentication method sets up authentication services within the app and configures the JWT Bearer handler as the default authentication method. The xref:Microsoft.Identity.Web.MicrosoftIdentityWebApiAuthenticationBuilderExtensions.AddMicrosoftIdentityWebApi%2A method configures services to protect the web API with Microsoft identity platform v2.0. This method expects an AzureAdB2C section in the app's configuration with the necessary settings to initialize authentication options.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
xref:Microsoft.AspNetCore.Builder.AuthAppBuilderExtensions.UseAuthentication%2A and xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A ensure that:
app.UseAuthorization();
This section pertains to the solution's :::no-loc text="Server"::: app.
The WeatherForecast controller (Controllers/WeatherForecastController.cs) exposes a protected API with the [Authorize] attribute applied to the controller. It's important to understand that:
[Authorize] attribute in this API controller is the only thing that protects this API from unauthorized access.[Authorize] attribute used in the Blazor WebAssembly app only serves as a hint to the app that the user should be authorized for the app to work correctly.[Authorize]
[ApiController]
[Route("[controller]")]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAdB2C:Scopes")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
...
}
}
wwwroot/appsettings.json configurationThis section pertains to the solution's :::no-loc text="Client"::: app.
Configuration is supplied by the wwwroot/appsettings.json file:
{
"AzureAdB2C": {
"Authority": "{AAD B2C INSTANCE}{TENANT DOMAIN}/{SIGN UP OR SIGN IN POLICY}",
"ClientId": "{CLIENT APP CLIENT ID}",
"ValidateAuthority": false
}
}
In the preceding configuration, the {AAD B2C INSTANCE} includes a trailing slash.
Example:
{
"AzureAdB2C": {
"Authority": "https://contoso.b2clogin.com/contoso.onmicrosoft.com/B2C_1_signupsignin1",
"ClientId": "11112222-bbbb-3333-cccc-4444dddd5555",
"ValidateAuthority": false
}
}
This section pertains to the solution's :::no-loc text="Client"::: app.
When an app is created to use an Individual B2C Account (IndividualB2C), the app automatically receives a package reference for the Microsoft Authentication Library (Microsoft.Authentication.WebAssembly.Msal). The package provides a set of primitives that help the app authenticate users and obtain tokens to call protected APIs.
If adding authentication to an app, manually add the Microsoft.Authentication.WebAssembly.Msal package to the app.
The Microsoft.Authentication.WebAssembly.Msal package transitively adds the Microsoft.AspNetCore.Components.WebAssembly.Authentication package to the app.
This section pertains to the solution's :::no-loc text="Client"::: app.
Support for xref:System.Net.Http.HttpClient instances is added that include access tokens when making requests to the server project.
In the Program file:
builder.Services.AddHttpClient("{PROJECT NAME}.ServerAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("{PROJECT NAME}.ServerAPI"));
The {PROJECT NAME} placeholder is the project name at solution creation. For example, providing a project name of BlazorSample produces a named xref:System.Net.Http.HttpClient of BlazorSample.ServerAPI.
Support for authenticating users is registered in the service container with the xref:Microsoft.Extensions.DependencyInjection.MsalWebAssemblyServiceCollectionExtensions.AddMsalAuthentication%2A extension method provided by the Microsoft.Authentication.WebAssembly.Msal package. This method sets up the services required for the app to interact with the Identity Provider (IP).
In the Program file:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}");
});
The {SCOPE URI} is the default access token scope (for example, https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/API.Access or the custom URI that you configured in the Azure portal).
The xref:Microsoft.Extensions.DependencyInjection.MsalWebAssemblyServiceCollectionExtensions.AddMsalAuthentication%2A method accepts a callback to configure the parameters required to authenticate an app. The values required for configuring the app can be obtained from the Azure portal AAD configuration when you register the app.
This section pertains to the solution's :::no-loc text="Client"::: app.
The default access token scopes represent the list of access token scopes that are:
All scopes must belong to the same app per Microsoft Entra ID rules. Additional scopes can be added for additional API apps as needed:
builder.Services.AddMsalAuthentication(options =>
{
...
options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}");
});
Specify additional scopes with AdditionalScopesToConsent:
options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}");
[!NOTE] xref:Microsoft.Authentication.WebAssembly.Msal.Models.MsalProviderOptions.AdditionalScopesToConsent%2A isn't able to provision delegated user permissions for Microsoft Graph via the Microsoft Entra ID consent UI when a user first uses an app registered in Microsoft Azure. For more information, see xref:blazor/security/webassembly/graph-api?pivots=graph-sdk-5#defaultaccesstokenscopes-versus-additionalscopestoconsent.
Example default access token scope:
options.ProviderOptions.DefaultAccessTokenScopes.Add(
"https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/API.Access");
For more information, see the following sections of the Additional scenarios article:
This section pertains to the solution's :::no-loc text="Client"::: app.
This section pertains to the solution's :::no-loc text="Client"::: app.
The xref:Microsoft.AspNetCore.Components.Authorization?displayProperty=fullName namespace is made available throughout the app via the _Imports.razor file:
...
@using Microsoft.AspNetCore.Components.Authorization
...
This section pertains to the solution's :::no-loc text="Client"::: app.
This section pertains to the solution's :::no-loc text="Client"::: app.
The App component (App.razor) is similar to the App component found in Blazor Server apps:
RedirectToLogin component.RedirectToLogin component manages redirecting unauthorized users to the login page.Due to changes in the framework across releases of ASP.NET Core, Razor markup for the App component (App.razor) isn't shown in this section. To inspect the markup of the component for a given release, use either of the following approaches:
Create an app provisioned for authentication from the default Blazor WebAssembly project template for the version of ASP.NET Core that you intend to use. Inspect the App component (App.razor) in the generated app.
Inspect the App component (App.razor) in reference source. Select the version from the branch selector, and search for the component in the ProjectTemplates folder of the repository because the App component's location has changed over the years.
This section pertains to the solution's :::no-loc text="Client"::: app.
This section pertains to the solution's :::no-loc text="Client"::: app.
This section pertains to the solution's :::no-loc text="Client"::: app.
This section pertains to the solution's :::no-loc text="Client"::: app.