From 8e739a9c6ea0c6466bf5eb8ee8eaf2d2597bec54 Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Tue, 28 Feb 2023 06:10:11 -0600 Subject: [PATCH] Hosted Blazor WASM AAD updates (#28515) --- .../hosted-with-azure-active-directory.md | 600 +++++++++++------- 1 file changed, 355 insertions(+), 245 deletions(-) diff --git a/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md b/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md index 84b68b6238bf..f5b4c89a3ab1 100644 --- a/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md +++ b/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md @@ -5,24 +5,34 @@ description: Learn how to secure a hosted ASP.NET Core Blazor WebAssembly app wi monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: "devx-track-csharp, mvc" -ms.date: 02/27/2023 +ms.date: 02/28/2023 uid: blazor/security/webassembly/hosted-with-azure-active-directory --- # Secure a hosted ASP.NET Core Blazor WebAssembly app with Azure Active Directory -This article explains how to create a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly) that uses [Azure Active Directory (AAD)](https://azure.microsoft.com/services/active-directory/) for authentication. +This article explains how to create a [hosted Blazor WebAssembly solution](xref:blazor/hosting-models#blazor-webassembly) that uses [Azure Active Directory (AAD)](https://azure.microsoft.com/services/active-directory/) for authentication. This article focuses on a single tenant app with a single tenant Azure app registration. -For more information on *solutions*, see . +Security articles for Blazor WebAssembly in this node don't cover a *hosted Blazor WebAssembly solution* with a *multi-tenant Azure registration*. Multi-tenant guidance found in for configuring a standalone Blazor WebAssembly app applies to the :::no-loc text="Client"::: app of a hosted Blazor WebAssembly solution. For additional information, see [Making your application multi-tenant](/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant). :::moniker range=">= aspnetcore-7.0" -## Register apps in AAD and create solution +## Walkthrough -### Create a tenant +The subsections of this node explain how to: + +* Create a tenant in Azure +* Register a server API app in Azure +* Register a client app in Azure +* Create the Blazor app +* Modify the **:::no-loc text="Server":::** `appsettings.json` configuration +* Modify the default access token scope scheme +* Run the app + +### Create a tenant in Azure Follow the guidance in [Quickstart: Set up a tenant](/azure/active-directory/develop/quickstart-create-new-tenant) to create a tenant in AAD. -### Register a server API app +### Register a server API app in Azure Register an AAD app for the *Server API app*: @@ -39,7 +49,7 @@ Record the following information: * Directory (tenant) ID (for example, `e86c78e2-8bb4-4c41-aefd-918e0565a45e`) * AAD Primary/Publisher/Tenant domain (for example, `contoso.onmicrosoft.com`): The domain is available as the **Publisher domain** in the **Branding** blade of the Azure portal for the registered app. -In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the app doesn't require sign in or user profile access. +In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the server API app doesn't require additional API access for merely signing in users and calling server API endpoints. In **Expose an API**: @@ -56,7 +66,7 @@ Record the following information: * App ID URI (for example, `api://41451fa7-82d9-4673-8fa5-69eff5a761fd`, `https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd`, or the custom value that you provide) * Scope name (for example, `API.Access`) -### Register a client app +### Register a client app in Azure Register an AAD app for the *Client app*: @@ -91,7 +101,7 @@ In **API permissions**: [!INCLUDE[](~/blazor/security/includes/authorize-client-app.md)] -### Create the app +### Create the Blazor app In an empty folder, replace the placeholders in the following command with the information recorded earlier and execute the command in a command shell: @@ -116,67 +126,85 @@ dotnet new blazorwasm -au SingleOrg --api-client-id "{SERVER API APP CLIENT ID}" The output location specified with the `-o|--output` option creates a project folder if it doesn't exist and becomes part of the app's name. **Avoid using dashes (`-`) in the app name that break the formation of the OIDC app identifier (see the earlier WARNING).** -> [!NOTE] -> A configuration change might be required when using an Azure tenant with an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), which is described in the [App settings](#app-settings-server-project) section. - -## **:::no-loc text="Server":::** app configuration - -*This section pertains to the solution's **:::no-loc text="Server":::** app.* - -### Authentication package - -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`](https://www.nuget.org/packages/Microsoft.Identity.Web) package. +### Modify the **:::no-loc text="Server":::** `appsettings.json` configuration -[!INCLUDE[](~/includes/package-reference.md)] +In the `appsettings.json` file of **:::no-loc text="Server":::** app, add the following audience entry to the `AzureAd` configuration: -The **:::no-loc text="Server":::** app of a hosted Blazor solution created from the Blazor WebAssembly template includes the [`Microsoft.Identity.Web.UI`](https://www.nuget.org/packages/Microsoft.Identity.Web) package by default. 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. +```json +"Audience": "https://{TENANT DOMAIN}/{SERVER API APP CLIENT ID}" +``` -### Authentication service support +A complete example of `AzureAd` configuration follows, where: -The `AddAuthentication` method sets up authentication services within the app and configures the JWT Bearer handler as the default authentication method. The method configures services to protect the web API with Microsoft Identity Platform v2.0. This method expects an `AzureAd` section in the app's configuration with the necessary settings to initialize authentication options. +* The tenant domain (`{TENANT DOMAIN}`) is `contoso.onmicrosoft.com`. +* The server API app client ID (`{SERVER API APP CLIENT ID}`) is `41451fa7-82d9-4673-8fa5-69eff5a761fd`. -```csharp -builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); +```json +{ + "AzureAd": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "contoso.onmicrosoft.com", + "TenantId": "e86c78e2-8bb4-4c41-aefd-918e0565a45e", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "CallbackPath": "/signin-oidc", + "Scopes": "API.Access", + "Audience": "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" + } +} ``` -[!INCLUDE[](~/blazor/includes/default-scheme.md)] +### Modify the default access token scope scheme - and ensure that: +The Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. -* The app attempts to parse and validate tokens on incoming requests. -* Any request attempting to access a protected resource without proper credentials fails. +When generating an app from the [Blazor project template](xref:blazor/project-structure), confirm that the value of the default access token scope in `Program.cs` of the **:::no-loc text="Client":::** app uses either the correct custom App ID URI value that you provided in the Azure portal or a value with **one** of the following formats: -```csharp -app.UseAuthentication(); -app.UseAuthorization(); -``` +* When the publisher domain of the directory is **trusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -### User.Identity.Name + ```csharp + options.ProviderOptions.DefaultAccessTokenScopes.Add( + "api://41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); + ``` -By default, 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, `2d64b3da-d9d5-42c6-9352-53d8df33d770@contoso.onmicrosoft.com`). + ```diff + - "api://api://..." + + "api://..." + ``` -To configure the app to receive the value from the `name` claim type: + **Inspect the value for a double scheme (`api://api://...`). If a double scheme is present, **remove the first `api://` scheme from the value**. -* Add a namespace for to `Program.cs`: +* When the publisher domain of the directory is **untrusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: ```csharp - using Microsoft.AspNetCore.Authentication.JwtBearer; + options.ProviderOptions.DefaultAccessTokenScopes.Add( + "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); ``` -* Configure the of the in `Program.cs`: + **Inspect the value for a double scheme (`api://https://...`). If a double scheme is present, **remove the first `api://` scheme from the value**. - ```csharp - builder.Services.Configure( - JwtBearerDefaults.AuthenticationScheme, options => - { - options.TokenValidationParameters.NameClaimType = "name"; - }); + ```diff + - "api://https://..." + + "https://..." ``` -### App settings (**`Server`** project) +The double-added scheme produced by the Blazor project template might be addressed in a future release. For more information, see [Double scheme for App ID URI with Blazor WASM template (hosted, single org) (dotnet/aspnetcore #27417)](https://github.com/dotnet/aspnetcore/issues/27417). -The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens: +### Run the app + +Run the app from the **:::no-loc text="Server":::** app. When using Visual Studio, either: + +* Set the **Startup Projects** drop down list in the toolbar to the *Server API app* and select the **Run** button. +* Select the **:::no-loc text="Server":::** app in **Solution Explorer** and select the **Run** button in the toolbar or start the app from the **Debug** menu. + +## Parts of the solution + +The following subsections explain the parts of a project generated from the Blazor WebAssembly project template. + +### `appsettings.json` configuration + +*This 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. Add the following audience entry to the `AzureAd` configuration: ```json { @@ -187,7 +215,7 @@ The `appsettings.json` file contains the options to configure the JWT bearer han "ClientId": "{SERVER API APP CLIENT ID}", "CallbackPath": "/signin-oidc", "Scopes": "{SCOPES}", - "Audience": "https://guardrexorg.onmicrosoft.com/{SERVER API APP CLIENT ID}" + "Audience": "https://{TENANT DOMAIN}/{SERVER API APP CLIENT ID}" } } ``` @@ -203,13 +231,48 @@ Example: "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", "CallbackPath": "/signin-oidc", "Scopes": "API.Access", - "Audience": "https://guardrexorg.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" + "Audience": "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" } } ``` +### Authentication package + +*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`](https://www.nuget.org/packages/Microsoft.Identity.Web) package. + +[!INCLUDE[](~/includes/package-reference.md)] + +The **:::no-loc text="Server":::** app of a hosted Blazor solution created from the Blazor WebAssembly template includes the [`Microsoft.Identity.Web.UI`](https://www.nuget.org/packages/Microsoft.Identity.Web) package by default. 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. + +### Authentication service support + +*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 method configures services to protect the web API with Microsoft Identity Platform v2.0. This method expects an `AzureAd` section in the app's configuration with the necessary settings to initialize authentication options. + +```csharp +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); +``` + +[!INCLUDE[](~/blazor/includes/default-scheme.md)] + + and ensure that: + +* The app attempts to parse and validate tokens on incoming requests. +* Any request attempting to access a protected resource without proper credentials fails. + +```csharp +app.UseAuthentication(); +app.UseAuthorization(); +``` + ### WeatherForecast controller +*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](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) applied to the controller. It's **important** to understand that: * The [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) in this API controller is the only thing that protect this API from unauthorized access. @@ -230,12 +293,38 @@ public class WeatherForecastController : ControllerBase } ``` -## **:::no-loc text="Client":::** app configuration +### `wwwroot/appsettings.json` configuration *This section pertains to the solution's **:::no-loc text="Client":::** app.* +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/{TENANT ID}", + "ClientId": "{CLIENT APP CLIENT ID}", + "ValidateAuthority": true + } +} +``` + +Example: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/e86c78e2-...-918e0565a45e", + "ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538", + "ValidateAuthority": true + } +} +``` + ### Authentication package +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + When an app is created to use Work or School Accounts (`SingleOrg`), the app automatically receives a package reference for the [Microsoft Authentication Library](/azure/active-directory/develop/msal-overview) ([`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/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`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package to the app. @@ -246,7 +335,9 @@ The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages ### Authentication service support -Support for instances is added that include access tokens when making requests to the server project. +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + +Support for instances is added that include access tokens when making requests to the **:::no-loc text="Server":::** app. `Program.cs`: @@ -275,34 +366,10 @@ builder.Services.AddMsalAuthentication(options => The 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. -### App settings (**`Client`** project) - -Configuration is supplied by the `wwwroot/appsettings.json` file: - -```json -{ - "AzureAd": { - "Authority": "https://login.microsoftonline.com/{TENANT ID}", - "ClientId": "{CLIENT APP CLIENT ID}", - "ValidateAuthority": true - } -} -``` - -Example: - -```json -{ - "AzureAd": { - "Authority": "https://login.microsoftonline.com/e86c78e2-...-918e0565a45e", - "ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538", - "ValidateAuthority": true - } -} -``` - ### Access token scopes +*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: * Included by default in the sign in request. @@ -318,29 +385,6 @@ builder.Services.AddMsalAuthentication(options => }); ``` -> [!NOTE] -> The Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. When generating an app from the [Blazor project template](xref:blazor/project-structure), confirm that the value of the default access token scope uses either the correct custom App ID URI value that you provided in the Azure portal or a value with **one** of the following formats: -> -> * When the publisher domain of the directory is **trusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -> -> ```csharp -> options.ProviderOptions.DefaultAccessTokenScopes.Add( -> "api://41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); -> ``` -> -> Inspect the value for a double scheme (`api://api://...`). If a double scheme is present, remove the first `api://` scheme from the value. -> -> * When the publisher domain of the directory is **untrusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -> -> ```csharp -> options.ProviderOptions.DefaultAccessTokenScopes.Add( -> "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); -> ``` -> -> Inspect the value for an extra `api://` scheme (`api://https://contoso.onmicrosoft.com/...`). If an extra `api://` scheme is present, remove the `api://` scheme from the value. -> -> The Blazor WebAssembly template might be changed in a future release of ASP.NET Core to address these scenarios. For more information, see [Double scheme for App ID URI with Blazor WASM template (hosted, single org) (dotnet/aspnetcore #27417)](https://github.com/dotnet/aspnetcore/issues/27417). - Specify additional scopes with `AdditionalScopesToConsent`: ```csharp @@ -354,61 +398,73 @@ For more information, see the following sections of the *Additional scenarios* a ### Login mode +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] ### Imports file +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/imports-file-hosted.md)] ### Index page +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] ### App component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/app-component.md)] ### RedirectToLogin component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/7.0/redirecttologin-component.md)] ### LoginDisplay component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] ### Authentication component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] ### FetchData component -[!INCLUDE[](~/blazor/security/includes/fetchdata-component.md)] - -## Run the app - -Run the app from the Server project. When using Visual Studio, either: - -* Set the **Startup Projects** drop down list in the toolbar to the *Server API app* and select the **Run** button. -* Select the Server project in **Solution Explorer** and select the **Run** button in the toolbar or start the app from the **Debug** menu. +*This section pertains to the solution's **:::no-loc text="Client":::** app.* - + ```csharp + builder.Services.Configure( + JwtBearerDefaults.AuthenticationScheme, options => + { + options.TokenValidationParameters.NameClaimType = "name"; + }); + ``` [!INCLUDE[](~/blazor/security/includes/7.0/troubleshoot.md)] @@ -427,13 +483,23 @@ Run the app from the Server project. When using Visual Studio, either: :::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" -## Register apps in AAD and create solution +## Walkthrough -### Create a tenant +The subsections of this node explain how to: + +* Create a tenant in Azure +* Register a server API app in Azure +* Register a client app in Azure +* Create the Blazor app +* Modify the **:::no-loc text="Server":::** `appsettings.json` configuration +* Modify the default access token scope scheme +* Run the app + +### Create a tenant in Azure Follow the guidance in [Quickstart: Set up a tenant](/azure/active-directory/develop/quickstart-create-new-tenant) to create a tenant in AAD. -### Register a server API app +### Register a server API app in Azure Register an AAD app for the *Server API app*: @@ -450,7 +516,7 @@ Record the following information: * Directory (tenant) ID (for example, `e86c78e2-8bb4-4c41-aefd-918e0565a45e`) * AAD Primary/Publisher/Tenant domain (for example, `contoso.onmicrosoft.com`): The domain is available as the **Publisher domain** in the **Branding** blade of the Azure portal for the registered app. -In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the app doesn't require sign in or user profile access. +In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the server API app doesn't require additional API access for merely signing in users and calling server API endpoints. In **Expose an API**: @@ -467,7 +533,7 @@ Record the following information: * App ID URI (for example, `api://41451fa7-82d9-4673-8fa5-69eff5a761fd`, `https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd`, or the custom value that you provide) * Scope name (for example, `API.Access`) -### Register a client app +### Register a client app in Azure Register an AAD app for the *Client app*: @@ -502,7 +568,7 @@ In **API permissions**: [!INCLUDE[](~/blazor/security/includes/authorize-client-app.md)] -### Create the app +### Create the Blazor app In an empty folder, replace the placeholders in the following command with the information recorded earlier and execute the command in a command shell: @@ -527,65 +593,85 @@ dotnet new blazorwasm -au SingleOrg --api-client-id "{SERVER API APP CLIENT ID}" The output location specified with the `-o|--output` option creates a project folder if it doesn't exist and becomes part of the app's name. **Avoid using dashes (`-`) in the app name that break the formation of the OIDC app identifier (see the earlier WARNING).** -> [!NOTE] -> A configuration change might be required when using an Azure tenant with an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), which is described in the [App settings](#app-settings-server-project) section. - -## **:::no-loc text="Server":::** app configuration +### Modify the **:::no-loc text="Server":::** `appsettings.json` configuration -*This section pertains to the solution's **:::no-loc text="Server":::** app.* +In the `appsettings.json` file of **:::no-loc text="Server":::** app, add the following audience entry to the `AzureAd` configuration: -### Authentication package - -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`](https://www.nuget.org/packages/Microsoft.Identity.Web) package. - -[!INCLUDE[](~/includes/package-reference.md)] - -The **:::no-loc text="Server":::** app of a hosted Blazor solution created from the Blazor WebAssembly template includes the [`Microsoft.Identity.Web.UI`](https://www.nuget.org/packages/Microsoft.Identity.Web) package by default. 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. +```json +"Audience": "https://{TENANT DOMAIN}/{SERVER API APP CLIENT ID}" +``` -### Authentication service support +A complete example of `AzureAd` configuration follows, where: -The `AddAuthentication` method sets up authentication services within the app and configures the JWT Bearer handler as the default authentication method. The method configures services to protect the web API with Microsoft Identity Platform v2.0. This method expects an `AzureAd` section in the app's configuration with the necessary settings to initialize authentication options. +* The tenant domain (`{TENANT DOMAIN}`) is `contoso.onmicrosoft.com`. +* The server API app client ID (`{SERVER API APP CLIENT ID}`) is `41451fa7-82d9-4673-8fa5-69eff5a761fd`. -```csharp -builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); +```json +{ + "AzureAd": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "contoso.onmicrosoft.com", + "TenantId": "e86c78e2-8bb4-4c41-aefd-918e0565a45e", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "CallbackPath": "/signin-oidc", + "Scopes": "API.Access", + "Audience": "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" + } +} ``` - and ensure that: +### Modify the default access token scope scheme -* The app attempts to parse and validate tokens on incoming requests. -* Any request attempting to access a protected resource without proper credentials fails. +The Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. -```csharp -app.UseAuthentication(); -app.UseAuthorization(); -``` +When generating an app from the [Blazor project template](xref:blazor/project-structure), confirm that the value of the default access token scope in `Program.cs` of the **:::no-loc text="Client":::** app uses either the correct custom App ID URI value that you provided in the Azure portal or a value with **one** of the following formats: -### User.Identity.Name +* When the publisher domain of the directory is **trusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -By default, 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, `2d64b3da-d9d5-42c6-9352-53d8df33d770@contoso.onmicrosoft.com`). + ```csharp + options.ProviderOptions.DefaultAccessTokenScopes.Add( + "api://41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); + ``` -To configure the app to receive the value from the `name` claim type: + ```diff + - "api://api://..." + + "api://..." + ``` -* Add a namespace for to `Program.cs`: + **Inspect the value for a double scheme (`api://api://...`). If a double scheme is present, **remove the first `api://` scheme from the value**. + +* When the publisher domain of the directory is **untrusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: ```csharp - using Microsoft.AspNetCore.Authentication.JwtBearer; + options.ProviderOptions.DefaultAccessTokenScopes.Add( + "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); ``` -* Configure the of the in `Program.cs`: + **Inspect the value for a double scheme (`api://https://...`). If a double scheme is present, **remove the first `api://` scheme from the value**. - ```csharp - builder.Services.Configure( - JwtBearerDefaults.AuthenticationScheme, options => - { - options.TokenValidationParameters.NameClaimType = "name"; - }); + ```diff + - "api://https://..." + + "https://..." ``` -### App settings (**`Server`** project) +The double-added scheme produced by the Blazor project template might be addressed in a future release. For more information, see [Double scheme for App ID URI with Blazor WASM template (hosted, single org) (dotnet/aspnetcore #27417)](https://github.com/dotnet/aspnetcore/issues/27417). -The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens: +### Run the app + +Run the app from the **:::no-loc text="Server":::** app. When using Visual Studio, either: + +* Set the **Startup Projects** drop down list in the toolbar to the *Server API app* and select the **Run** button. +* Select the **:::no-loc text="Server":::** app in **Solution Explorer** and select the **Run** button in the toolbar or start the app from the **Debug** menu. + +## Parts of the solution + +The following subsections explain the parts of a project generated from the Blazor WebAssembly project template. + +### `appsettings.json` configuration + +*This 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. Add the following audience entry to the `AzureAd` configuration: ```json { @@ -596,7 +682,7 @@ The `appsettings.json` file contains the options to configure the JWT bearer han "ClientId": "{SERVER API APP CLIENT ID}", "CallbackPath": "/signin-oidc", "Scopes": "{SCOPES}", - "Audience": "https://guardrexorg.onmicrosoft.com/{SERVER API APP CLIENT ID}" + "Audience": "https://{TENANT DOMAIN}/{SERVER API APP CLIENT ID}" } } ``` @@ -612,13 +698,48 @@ Example: "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", "CallbackPath": "/signin-oidc", "Scopes": "API.Access", - "Audience": "https://guardrexorg.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" + "Audience": "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd" } } ``` +### Authentication package + +*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`](https://www.nuget.org/packages/Microsoft.Identity.Web) package. + +[!INCLUDE[](~/includes/package-reference.md)] + +The **:::no-loc text="Server":::** app of a hosted Blazor solution created from the Blazor WebAssembly template includes the [`Microsoft.Identity.Web.UI`](https://www.nuget.org/packages/Microsoft.Identity.Web) package by default. 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. + +### Authentication service support + +*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 method configures services to protect the web API with Microsoft Identity Platform v2.0. This method expects an `AzureAd` section in the app's configuration with the necessary settings to initialize authentication options. + +```csharp +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); +``` + +[!INCLUDE[](~/blazor/includes/default-scheme.md)] + + and ensure that: + +* The app attempts to parse and validate tokens on incoming requests. +* Any request attempting to access a protected resource without proper credentials fails. + +```csharp +app.UseAuthentication(); +app.UseAuthorization(); +``` + ### WeatherForecast controller +*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](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) applied to the controller. It's **important** to understand that: * The [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) in this API controller is the only thing that protect this API from unauthorized access. @@ -639,12 +760,38 @@ public class WeatherForecastController : ControllerBase } ``` -## **:::no-loc text="Client":::** app configuration +### `wwwroot/appsettings.json` configuration *This section pertains to the solution's **:::no-loc text="Client":::** app.* +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/{TENANT ID}", + "ClientId": "{CLIENT APP CLIENT ID}", + "ValidateAuthority": true + } +} +``` + +Example: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/e86c78e2-...-918e0565a45e", + "ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538", + "ValidateAuthority": true + } +} +``` + ### Authentication package +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + When an app is created to use Work or School Accounts (`SingleOrg`), the app automatically receives a package reference for the [Microsoft Authentication Library](/azure/active-directory/develop/msal-overview) ([`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/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`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package to the app. @@ -655,7 +802,9 @@ The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages ### Authentication service support -Support for instances is added that include access tokens when making requests to the server project. +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + +Support for instances is added that include access tokens when making requests to the **:::no-loc text="Server":::** app. `Program.cs`: @@ -684,34 +833,10 @@ builder.Services.AddMsalAuthentication(options => The 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. -### App settings (**`Client`** project) - -Configuration is supplied by the `wwwroot/appsettings.json` file: - -```json -{ - "AzureAd": { - "Authority": "https://login.microsoftonline.com/{TENANT ID}", - "ClientId": "{CLIENT APP CLIENT ID}", - "ValidateAuthority": true - } -} -``` - -Example: - -```json -{ - "AzureAd": { - "Authority": "https://login.microsoftonline.com/e86c78e2-...-918e0565a45e", - "ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538", - "ValidateAuthority": true - } -} -``` - ### Access token scopes +*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: * Included by default in the sign in request. @@ -727,29 +852,6 @@ builder.Services.AddMsalAuthentication(options => }); ``` -> [!NOTE] -> The Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. When generating an app from the [Blazor project template](xref:blazor/project-structure), confirm that the value of the default access token scope uses either the correct custom App ID URI value that you provided in the Azure portal or a value with **one** of the following formats: -> -> * When the publisher domain of the directory is **trusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -> -> ```csharp -> options.ProviderOptions.DefaultAccessTokenScopes.Add( -> "api://41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); -> ``` -> -> Inspect the value for a double scheme (`api://api://...`). If a double scheme is present, remove the first `api://` scheme from the value. -> -> * When the publisher domain of the directory is **untrusted**, the default access token scope is typically a value similar to the following example, where `API.Access` is the default scope name: -> -> ```csharp -> options.ProviderOptions.DefaultAccessTokenScopes.Add( -> "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access"); -> ``` -> -> Inspect the value for an extra `api://` scheme (`api://https://contoso.onmicrosoft.com/...`). If an extra `api://` scheme is present, remove the `api://` scheme from the value. -> -> The Blazor WebAssembly template might be changed in a future release of ASP.NET Core to address these scenarios. For more information, see [Double scheme for App ID URI with Blazor WASM template (hosted, single org) (dotnet/aspnetcore #27417)](https://github.com/dotnet/aspnetcore/issues/27417). - Specify additional scopes with `AdditionalScopesToConsent`: ```csharp @@ -763,61 +865,73 @@ For more information, see the following sections of the *Additional scenarios* a ### Login mode +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] ### Imports file +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/imports-file-hosted.md)] ### Index page +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] ### App component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/app-component.md)] ### RedirectToLogin component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/6.0/redirecttologin-component.md)] ### LoginDisplay component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] ### Authentication component +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] ### FetchData component -[!INCLUDE[](~/blazor/security/includes/fetchdata-component.md)] - -## Run the app - -Run the app from the Server project. When using Visual Studio, either: +*This section pertains to the solution's **:::no-loc text="Client":::** app.* -* Set the **Startup Projects** drop down list in the toolbar to the *Server API app* and select the **Run** button. -* Select the Server project in **Solution Explorer** and select the **Run** button in the toolbar or start the app from the **Debug** menu. +[!INCLUDE[](~/blazor/security/includes/fetchdata-component.md)] - + ```csharp + builder.Services.Configure( + JwtBearerDefaults.AuthenticationScheme, options => + { + options.TokenValidationParameters.NameClaimType = "name"; + }); + ``` [!INCLUDE[](~/blazor/security/includes/6.0/troubleshoot.md)] @@ -829,8 +943,7 @@ Run the app from the Server project. When using Visual Studio, either: * * * [Microsoft identity platform documentation](/azure/active-directory/develop/) -* [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app) -* [Security best practices for application properties in Azure Active Directory](/azure/active-directory/develop/security-best-practices-for-app-registration) +* [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app)* * * * [Security best practices for application properties in Azure Active Directory](/azure/active-directory/develop/security-best-practices-for-app-registration) :::moniker-end @@ -859,7 +972,7 @@ Record the following information: * Directory (tenant) ID (for example, `e86c78e2-8bb4-4c41-aefd-918e0565a45e`) * AAD Primary/Publisher/Tenant domain (for example, `contoso.onmicrosoft.com`): The domain is available as the **Publisher domain** in the **Branding** blade of the Azure portal for the registered app. -In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the app doesn't require sign in or user profile access. +In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the server API app doesn't require additional API access for merely signing in users and calling server API endpoints. In **Expose an API**: @@ -936,9 +1049,6 @@ dotnet new blazorwasm -au SingleOrg --api-client-id "{SERVER API APP CLIENT ID}" The output location specified with the `-o|--output` option creates a project folder if it doesn't exist and becomes part of the app's name. **Avoid using dashes (`-`) in the app name that break the formation of the OIDC app identifier (see the earlier WARNING).** -> [!NOTE] -> A configuration change might be required when using an Azure tenant with an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), which is described in the [App settings](#app-settings-server-project) section. - ## **:::no-loc text="Server":::** app configuration *This section pertains to the solution's **:::no-loc text="Server":::** app.* @@ -992,7 +1102,7 @@ To configure the app to receive the value from the `name` claim type: }); ``` -### App settings (**`Server`** project) +### App settings (**:::no-loc text="Server":::** project) The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens: @@ -1265,7 +1375,7 @@ Record the following information: * Directory (tenant) ID (for example, `e86c78e2-8bb4-4c41-aefd-918e0565a45e`) * AAD Primary/Publisher/Tenant domain (for example, `contoso.onmicrosoft.com`): The domain is available as the **Publisher domain** in the **Branding** blade of the Azure portal for the registered app. -In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the app doesn't require sign in or user profile access. +In **API permissions**, remove the **Microsoft Graph** > **User.Read** permission, as the server API app doesn't require additional API access for merely signing in users and calling server API endpoints. In **Expose an API**: @@ -1396,7 +1506,7 @@ To configure the app to receive the value from the `name` claim type: }); ``` -### App settings (**`Server`** project) +### App settings (**:::no-loc text="Server":::** project) The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens: