diff --git a/aspnetcore/blazor/fundamentals/logging.md b/aspnetcore/blazor/fundamentals/logging.md index 3db2c5fb0916..efdcf0da063b 100644 --- a/aspnetcore/blazor/fundamentals/logging.md +++ b/aspnetcore/blazor/fundamentals/logging.md @@ -1745,6 +1745,43 @@ var connection = new HubConnectionBuilder() .Build(); ``` +## Blazor WebAssembly authentication logging + +*This section only applies to Blazor WebAssembly apps.* + +Log messages specific to for the or logging levels using ***either*** the following approaches: + +* Add a log filter for in `Program.cs`: + + ```csharp + #if DEBUG + builder.Logging.AddFilter( + "Microsoft.AspNetCore.Components.WebAssembly.Authentication", + LogLevel.Debug); + #endif + ``` + +* Provide an app settings file for the `Development` environment (`wwwroot/appsettings.Development.json`) with log level configuration: + + ```json + { + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.AspNetCore.Components.WebAssembly.Authentication": "Debug" + } + } + } + ``` + + Set the value to either "`Debug`" or "`Trace`". + +> [!NOTE] +> Blazor WebAssembly apps only log to the client-side [browser developer tools](https://developer.mozilla.org/docs/Glossary/Developer_Tools) console. + ## Additional resources * diff --git a/aspnetcore/blazor/security/includes/troubleshoot-60-or-earlier.md b/aspnetcore/blazor/security/includes/troubleshoot-60-or-earlier.md new file mode 100644 index 000000000000..4f95f917ef79 --- /dev/null +++ b/aspnetcore/blazor/security/includes/troubleshoot-60-or-earlier.md @@ -0,0 +1,138 @@ +## Troubleshoot + +### Common errors + +* Misconfiguration of the app or Identity Provider (IP) + + The most common errors are caused by incorrect configuration. The following are a few examples: + + * Depending on the requirements of the scenario, a missing or incorrect Authority, Instance, Tenant ID, Tenant domain, Client ID, or Redirect URI prevents an app from authenticating clients. + * An incorrect access token scope prevents clients from accessing server web API endpoints. + * Incorrect or missing server API permissions prevent clients from accessing server web API endpoints. + * Running the app at a different port than is configured in the Redirect URI of the Identity Provider's app registration. + + Configuration sections of this article's guidance show examples of the correct configuration. Carefully check each section of the article looking for app and IP misconfiguration. + + If the configuration appears correct: + + * Analyze application logs. + * Examine the network traffic between the client app and the IP or server app with the browser's developer tools. Often, an exact error message or a message with a clue to what's causing the problem is returned to the client by the IP or server app after making a request. Developer tools guidance is found in the following articles: + + * [Google Chrome](https://developers.google.com/web/tools/chrome-devtools/network) (Google documentation) + * [Microsoft Edge](/microsoft-edge/devtools-guide-chromium/network/) + * [Mozilla Firefox](https://developer.mozilla.org/docs/Tools/Network_Monitor) (Mozilla documentation) + + * Decode the contents of a JSON Web Token (JWT) used for authenticating a client or accessing a server web API, depending on where the problem is occurring. For more information, see [Inspect the content of a JSON Web Token (JWT)](#inspect-the-content-of-a-json-web-token-jwt). + + The documentation team responds to document feedback and bugs in articles (open an issue from the **This page** feedback section) but is unable to provide product support. Several public support forums are available to assist with troubleshooting an app. We recommend the following: + + * [Stack Overflow (tag: `blazor`)](https://stackoverflow.com/questions/tagged/blazor) + * [ASP.NET Core Slack Team](https://join.slack.com/t/aspnetcore/shared_invite/zt-1b60h73p0-PZPq3YCCaPbB21RcujMSVA) + * [Blazor Gitter](https://gitter.im/aspnet/Blazor) + + *The preceding forums are not owned or controlled by Microsoft.* + + For non-security, non-sensitive, and non-confidential reproducible framework bug reports, [open an issue with the ASP.NET Core product unit](https://github.com/dotnet/aspnetcore/issues). Don't open an issue with the product unit until you've thoroughly investigated the cause of a problem and can't resolve it on your own and with the help of the community on a public support forum. The product unit isn't able to troubleshoot individual apps that are broken due to simple misconfiguration or use cases involving third-party services. If a report is sensitive or confidential in nature or describes a potential security flaw in the product that attackers may exploit, see [Reporting security issues and bugs (dotnet/aspnetcore GitHub repository)](https://github.com/dotnet/aspnetcore/blob/main/CONTRIBUTING.md#reporting-security-issues-and-bugs). + +* Unauthorized client for AAD + + > info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] + > Authorization failed. These requirements were not met: + > DenyAnonymousAuthorizationRequirement: Requires an authenticated user. + + Login callback error from AAD: + + * Error: `unauthorized_client` + * Description: `AADB2C90058: The provided application is not configured to allow public clients.` + + To resolve the error: + + 1. In the Azure portal, access the [app's manifest](/azure/active-directory/develop/reference-app-manifest). + 1. Set the [`allowPublicClient` attribute](/azure/active-directory/develop/reference-app-manifest#allowpublicclient-attribute) to `null` or `true`. + +### Cookies and site data + +Cookies and site data can persist across app updates and interfere with testing and troubleshooting. Clear the following when making app code changes, user account changes with the provider, or provider app configuration changes: + +* User sign-in cookies +* App cookies +* Cached and stored site data + +One approach to prevent lingering cookies and site data from interfering with testing and troubleshooting is to: + +* Configure a browser + * Use a browser for testing that you can configure to delete all cookie and site data each time the browser is closed. + * Make sure that the browser is closed manually or by the IDE for any change to the app, test user, or provider configuration. +* Use a custom command to open a browser in incognito or private mode in Visual Studio: + * Open **Browse With** dialog box from Visual Studio's **Run** button. + * Select the **Add** button. + * Provide the path to your browser in the **Program** field. The following executable paths are typical installation locations for Windows 10. If your browser is installed in a different location or you aren't using Windows 10, provide the path to the browser's executable. + * Microsoft Edge: `C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe` + * Google Chrome: `C:\Program Files (x86)\Google\Chrome\Application\chrome.exe` + * Mozilla Firefox: `C:\Program Files\Mozilla Firefox\firefox.exe` + * In the **Arguments** field, provide the command-line option that the browser uses to open in incognito or private mode. Some browsers require the URL of the app. + * Microsoft Edge: Use `-inprivate`. + * Google Chrome: Use `--incognito --new-window {URL}`, where the placeholder `{URL}` is the URL to open (for example, `https://localhost:5001`). + * Mozilla Firefox: Use `-private -url {URL}`, where the placeholder `{URL}` is the URL to open (for example, `https://localhost:5001`). + * Provide a name in the **Friendly name** field. For example, `Firefox Auth Testing`. + * Select the **OK** button. + * To avoid having to select the browser profile for each iteration of testing with an app, set the profile as the default with the **Set as Default** button. + * Make sure that the browser is closed by the IDE for any change to the app, test user, or provider configuration. + +### App upgrades + +A functioning app may fail immediately after upgrading either the .NET Core SDK on the development machine or changing package versions within the app. In some cases, incoherent packages may break an app when performing major upgrades. Most of these issues can be fixed by following these instructions: + +1. Clear the local system's NuGet package caches by executing [`dotnet nuget locals all --clear`](/dotnet/core/tools/dotnet-nuget-locals) from a command shell. +1. Delete the project's `bin` and `obj` folders. +1. Restore and rebuild the project. +1. Delete all of the files in the deployment folder on the server prior to redeploying the app. + +> [!NOTE] +> Use of package versions incompatible with the app's target framework isn't supported. For information on a package, use the [NuGet Gallery](https://www.nuget.org) or [FuGet Package Explorer](https://www.fuget.org). + +### Run the Server app + +When testing and troubleshooting a hosted Blazor WebAssembly [solution](xref:blazor/tooling#visual-studio-solution-file-sln), make sure that you're running the app from the **:::no-loc text="Server":::** project. For example in Visual Studio, confirm that the Server project is highlighted in **Solution Explorer** before you start the app with any of the following approaches: + +* Select the **Run** button. +* Use **Debug** > **Start Debugging** from the menu. +* Press F5. + +### Inspect the user + +The [ASP.NET Core framework's test assets](https://github.com/dotnet/aspnetcore/tree/main/src/Components/WebAssembly/testassets) include a [Blazor WebAssembly client app](https://github.com/dotnet/aspnetcore/tree/main/src/Components/WebAssembly/testassets/Wasm.Authentication.Client) with a `User` component that can be useful in troubleshooting. The `User` component can be used directly in apps or serve as the basis for further customization: + +[`User` test component in the `dotnet/aspnetcore` GitHub repository](https://github.com/dotnet/aspnetcore/blob/main/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/Pages/User.razor) + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +### Inspect the content of a JSON Web Token (JWT) + +To decode a JSON Web Token (JWT), use Microsoft's [jwt.ms](https://jwt.ms/) tool. Values in the UI never leave your browser. + +Example encoded JWT (shortened for display): + +> eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q + +Example JWT decoded by the tool for an app that authenticates against Azure AAD B2C: + +```json +{ + "typ": "JWT", + "alg": "RS256", + "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk" +}.{ + "exp": 1610059429, + "nbf": 1610055829, + "ver": "1.0", + "iss": "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/", + "sub": "5ee963fb-24d6-4d72-a1b6-889c6e2c7438", + "aud": "70bde375-fce3-4b82-984a-b247d823a03f", + "nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871", + "iat": 1610055829, + "auth_time": 1610055822, + "idp": "idp.com", + "tfp": "B2C_1_signupsignin" +}.[Signature] +``` diff --git a/aspnetcore/blazor/security/includes/troubleshoot.md b/aspnetcore/blazor/security/includes/troubleshoot.md index 4f95f917ef79..f076ecb90231 100644 --- a/aspnetcore/blazor/security/includes/troubleshoot.md +++ b/aspnetcore/blazor/security/includes/troubleshoot.md @@ -1,5 +1,9 @@ ## Troubleshoot +### Logging + +To enable debug or trace logging for Blazor WebAssembly authentication, see . + ### Common errors * Misconfiguration of the app or Identity Provider (IP) diff --git a/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md b/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md index 935fc02dc365..3a0d2b5e4dfe 100644 --- a/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md +++ b/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory-b2c.md @@ -14,7 +14,7 @@ This article explains how to create a [hosted Blazor WebAssembly solution](xref: For more information on *solutions*, see . -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" ## Register apps in AAD B2C and create solution @@ -393,7 +393,7 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -784,7 +784,7 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -1169,6 +1169,399 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library) +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* +* [Tutorial: Create an Azure Active Directory B2C tenant](/azure/active-directory-b2c/tutorial-create-tenant) +* [Tutorial: Register an application in Azure Active Directory B2C](/azure/active-directory-b2c/tutorial-register-applications) +* [Microsoft identity platform documentation](/azure/active-directory/develop/) + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +## Register apps in AAD B2C and create solution + +### Create a tenant + +Follow the guidance in [Tutorial: Create an Azure Active Directory B2C tenant](/azure/active-directory-b2c/tutorial-create-tenant) to create an AAD B2C tenant. Return to this article immediately after creating or identifying a tenant to use. + +Record the AAD B2C instance (for example, `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. + +### Register a server API app + +Register an AAD B2C app for the *Server API app*: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Server AAD B2C**). +1. For **Supported account types**, select the multi-tenant option: **Accounts in any identity provider or organizational directory (for authenticating users with user flows)** +1. The *Server API app* doesn't require a **Redirect URI** in this scenario, so leave the drop down set to **Web** and don't enter a redirect URI. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), confirm that **Permissions** > **Grant admin consent to openid and offline_access permissions** is selected. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +Record the following information: + +* *Server API app* Application (client) ID (for example, `41451fa7-82d9-4673-8fa5-69eff5a761fd`) +* 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 **Expose an API**: + +1. Select **Add a scope**. +1. Select **Save and continue**. +1. Provide a **Scope name** (for example, `API.Access`). +1. Provide an **Admin consent display name** (for example, `Access API`). +1. Provide an **Admin consent description** (for example, `Allows the app to access server app API endpoints.`). +1. Confirm that the **State** is set to **Enabled**. +1. Select **Add scope**. + +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 provided) +* Scope name (for example, `API.Access`) + +### Register a client app + +Register an AAD B2C app for the *Client app*: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Client AAD B2C**). +1. For **Supported account types**, select the multi-tenant option: **Accounts in any identity provider or organizational directory (for authenticating users with user flows)** +1. Set the **Redirect URI** drop down to **Single-page application (SPA)** and provide the following redirect URI: `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. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), confirm that **Permissions** > **Grant admin consent to openid and offline_access permissions** is selected. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +> [!NOTE] +> Supplying the port number for a `localhost` AAD B2C redirect URI isn't required. For more information, see [Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Azure documentation)](/azure/active-directory/develop/reply-url#localhost-exceptions). + +Record the Application (client) ID (for example, `4369008b-21fa-427c-abaa-9b53bf58e538`). + +In **Authentication** > **Platform configurations** > **Single-page application (SPA)**: + +1. Confirm the **Redirect URI** of `https://localhost/authentication/login-callback` is present. +1. In the **Implicit grant** section, ensure that the checkboxes for **Access tokens** and **ID tokens** are **not** selected. +1. The remaining defaults for the app are acceptable for this experience. +1. Select the **Save** button. + +In **API permissions**: + +1. Select **Add a permission** followed by **My APIs**. +1. Select the *Server API app* from the **Name** column (for example, **Blazor Server AAD B2C**). +1. Open the **API** list. +1. Enable access to the API (for example, `API.Access`). +1. Select **Add permissions**. +1. Select the **Grant admin consent for {TENANT NAME}** button. Select **Yes** to confirm. + +[!INCLUDE[](~/blazor/security/includes/authorize-client-app.md)] + +In **Home** > **Azure AD B2C** > **User flows**: + +[Create a sign-up and sign-in user flow](/azure/active-directory-b2c/tutorial-create-user-flows) + +At a minimum, select the **Application claims** > **Display Name** user attribute to populate the `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_signupsignin`). + +### Create the app + +Replace the placeholders in the following command with the information recorded earlier and execute the command in a command shell: + +```dotnetcli +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}" --client-id "{CLIENT APP CLIENT ID}" --default-scope "{DEFAULT SCOPE}" --domain "{TENANT DOMAIN}" -ho -o {APP NAME} -ssp "{SIGN UP OR SIGN IN POLICY}" +``` + +> [!WARNING] +> **Avoid using dashes (`-`) in the app name `{APP 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)](https://github.com/dotnet/aspnetcore/issues/35337). + +| Placeholder | Azure portal name | Example | +| ----------------------------- | ----------------------------------------------------- | ------------------------------------------------------------- | +| `{AAD B2C INSTANCE}` | Instance | `https://contoso.b2clogin.com/` (includes the trailing slash) | +| `{APP NAME}` | — | `BlazorSample` | +| `{CLIENT APP CLIENT ID}` | Application (client) ID for the **:::no-loc text="Client":::** app | `4369008b-21fa-427c-abaa-9b53bf58e538` | +| `{DEFAULT SCOPE}` | Scope name | `API.Access` | +| `{SERVER API APP CLIENT ID}` | Application (client) ID for the *Server API app* | `41451fa7-82d9-4673-8fa5-69eff5a761fd` | +| `{SERVER API APP ID URI}` | Application ID URI† | `41451fa7-82d9-4673-8fa5-69eff5a761fd`† | +| `{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 Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. When providing the App ID URI for the `{SERVER API APP ID URI}` placeholder and if the scheme is `api://`, remove the scheme (`api://`) from the argument, as the example value in the preceding table shows. If the App ID URI is a custom value or has some other scheme (for example, `https://` for an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain) similar to `https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd`), you must manually update the default scope URI and remove the `api://` scheme after the **:::no-loc text="Client":::** app is created by the template. For more information, see the note in the [Access token scopes](#access-token-scopes) section. 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). + +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] +> The scope set up in a hosted Blazor WebAssembly solution by the [Blazor WebAssembly project template](xref:blazor/project-structure) might have the App ID URI host repeated. Confirm that the scope configured for the `DefaultAccessTokenScopes` collection is correct in `Program.cs` of the **:::no-loc text="Client":::** app. + +## **:::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. + +[!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 will never 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 + +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 `AzureAdB2C` section in the app's configuration with the necessary settings to initialize authentication options. + +```csharp +services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C")); +``` + + 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(); +``` + +### User.Identity.Name + +By default, the `User.Identity.Name` isn't populated. + +To configure the app to receive the value from the `name` claim type: + +* Add a namespace for to `Program.cs`: + + ```csharp + using Microsoft.AspNetCore.Authentication.JwtBearer; + ``` + +* Configure the of the in `Program.cs`: + + ```csharp + builder.Services.Configure( + JwtBearerDefaults.AuthenticationScheme, options => + { + options.TokenValidationParameters.NameClaimType = "name"; + }); + ``` + +### App settings + +The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens. + +```json +{ + "AzureAdB2C": { + "Instance": "https://{TENANT}.b2clogin.com/", + "ClientId": "{SERVER API APP CLIENT ID}", + "Domain": "{TENANT DOMAIN}", + "SignUpSignInPolicyId": "{SIGN UP OR SIGN IN POLICY}" + } +} +``` + +Example: + +```json +{ + "AzureAdB2C": { + "Instance": "https://contoso.b2clogin.com/", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "Domain": "contoso.onmicrosoft.com", + "SignUpSignInPolicyId": "B2C_1_signupsignin1", + } +} +``` + +### WeatherForecast controller + +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. +* The [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) 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. + +```csharp +[Authorize] +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + [HttpGet] + public IEnumerable Get() + { + ... + } +} +``` + +## **:::no-loc text="Client":::** app configuration + +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + +### Authentication package + +When an app is created to use an Individual B2C Account (`IndividualB2C`), 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. + +[!INCLUDE[](~/includes/package-reference.md)] + +The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package transitively adds the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +### Authentication service support + +Support for instances is added that include access tokens when making requests to the server project. + +`Program.cs`: + +```csharp +builder.Services.AddHttpClient("{APP ASSEMBLY}.ServerAPI", client => + client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) + .AddHttpMessageHandler(); + +builder.Services.AddScoped(sp => sp.GetRequiredService() + .CreateClient("{APP ASSEMBLY}.ServerAPI")); +``` + +The placeholder `{APP ASSEMBLY}` is the app's assembly name (for example, `BlazorSample.Client`). + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package. This method sets up the services required for the app to interact with the Identity Provider (IP). + +`Program.cs`: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +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. + +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "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: + +```json +{ + "AzureAdB2C": { + "Authority": "https://contoso.b2clogin.com/contoso.onmicrosoft.com/B2C_1_signupsignin1", + "ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538", + "ValidateAuthority": false + } +} +``` + +### Access token scopes + +The default access token scopes represent the list of access token scopes that are: + +* Included by default in the sign in request. +* Used to provision an access token immediately after authentication. + +All scopes must belong to the same app per Azure Active Directory rules. Additional scopes can be added for additional API apps as needed: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + ... + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +> [!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 +options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}"); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +### Login mode + +[!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] + +### Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-hosted.md)] + +### Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] + +### App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +### RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +### LoginDisplay component + +[!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] + +### Authentication component + +[!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. + +[!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] + +[!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources 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 78441ad5e0fd..49993481ad7b 100644 --- a/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md +++ b/aspnetcore/blazor/security/webassembly/hosted-with-azure-active-directory.md @@ -14,7 +14,7 @@ This article explains how to create a [hosted Blazor WebAssembly solution](xref: For more information on *solutions*, see . -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" ## Register apps in AAD and create solution @@ -386,7 +386,7 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -772,7 +772,7 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -1150,6 +1150,392 @@ Run the app from the Server project. When using Visual Studio, either: [!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library) +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* +* +* [Microsoft identity platform documentation](/azure/active-directory/develop/) +* [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app) + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +## Register apps in AAD and create solution + +### Create a tenant + +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 an AAD app for the *Server API app*: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Server AAD**). +1. Choose a **Supported account types**. You may select **Accounts in this organizational directory only** (single tenant) for this experience. +1. The *Server API app* doesn't require a **Redirect URI** in this scenario, so leave the drop down set to **Web** and don't enter a redirect URI. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), clear the **Permissions** > **Grant admin consent to openid and offline_access permissions** checkbox. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +Record the following information: + +* *Server API app* Application (client) ID (for example, `41451fa7-82d9-4673-8fa5-69eff5a761fd`) +* 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 **Expose an API**: + +1. Select **Add a scope**. +1. Select **Save and continue**. +1. Provide a **Scope name** (for example, `API.Access`). +1. Provide an **Admin consent display name** (for example, `Access API`). +1. Provide an **Admin consent description** (for example, `Allows the app to access server app API endpoints.`). +1. Confirm that the **State** is set to **Enabled**. +1. Select **Add scope**. + +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 an AAD app for the *Client app*: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Client AAD**). +1. Choose a **Supported account types**. You may select **Accounts in this organizational directory only** (single tenant) for this experience. +1. Set the **Redirect URI** drop down to **Single-page application (SPA)** and provide the following redirect URI: `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. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), clear the **Permissions** > **Grant admin consent to openid and offline_access permissions** checkbox. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +> [!NOTE] +> Supplying the port number for a `localhost` AAD redirect URI isn't required. For more information, see [Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Azure documentation)](/azure/active-directory/develop/reply-url#localhost-exceptions). + +Record the **:::no-loc text="Client":::** app Application (client) ID (for example, `4369008b-21fa-427c-abaa-9b53bf58e538`). + +In **Authentication** > **Platform configurations** > **Single-page application (SPA)**: + +1. Confirm the **Redirect URI** of `https://localhost/authentication/login-callback` is present. +1. In the **Implicit grant** section, ensure that the checkboxes for **Access tokens** and **ID tokens** are **not** selected. +1. The remaining defaults for the app are acceptable for this experience. +1. Select the **Save** button. + +In **API permissions**: + +1. Confirm that the app has **Microsoft Graph** > **User.Read** permission. +1. Select **Add a permission** followed by **My APIs**. +1. Select the *Server API app* from the **Name** column (for example, **Blazor Server AAD**). +1. Open the **API** list. +1. Enable access to the API (for example, `API.Access`). +1. Select **Add permissions**. +1. Select the **Grant admin consent for {TENANT NAME}** button. Select **Yes** to confirm. + +[!INCLUDE[](~/blazor/security/includes/authorize-client-app.md)] + +### Create the 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: + +```dotnetcli +dotnet new blazorwasm -au SingleOrg --api-client-id "{SERVER API APP CLIENT ID}" --app-id-uri "{SERVER API APP ID URI}" --client-id "{CLIENT APP CLIENT ID}" --default-scope "{DEFAULT SCOPE}" --domain "{TENANT DOMAIN}" -ho -o {APP NAME} --tenant-id "{TENANT ID}" +``` + +> [!WARNING] +> **Avoid using dashes (`-`) in the app name `{APP 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)](https://github.com/dotnet/aspnetcore/issues/35337). + +| Placeholder | Azure portal name | Example | +| ---------------------------- | ----------------------------------------------------- | ---------------------------------------------- | +| `{APP NAME}` | — | `BlazorSample` | +| `{CLIENT APP CLIENT ID}` | Application (client) ID for the **:::no-loc text="Client":::** app | `4369008b-21fa-427c-abaa-9b53bf58e538` | +| `{DEFAULT SCOPE}` | Scope name | `API.Access` | +| `{SERVER API APP CLIENT ID}` | Application (client) ID for the *Server API app* | `41451fa7-82d9-4673-8fa5-69eff5a761fd` | +| `{SERVER API APP ID URI}` | Application ID URI† | `41451fa7-82d9-4673-8fa5-69eff5a761fd`† | +| `{TENANT DOMAIN}` | Primary/Publisher/Tenant domain | `contoso.onmicrosoft.com` | +| `{TENANT ID}` | Directory (tenant) ID | `e86c78e2-8bb4-4c41-aefd-918e0565a45e` | + +†The Blazor WebAssembly template automatically adds a scheme of `api://` to the App ID URI argument passed in the `dotnet new` command. When providing the App ID URI for the `{SERVER API APP ID URI}` placeholder and if the scheme is `api://`, remove the scheme (`api://`) from the argument, as the example value in the preceding table shows. If the App ID URI is a custom value or has some other scheme (for example, `https://` for an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain) similar to `https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd`), you must manually update the default scope URI and remove the `api://` scheme after the **:::no-loc text="Client":::** app is created by the template. For more information, see the note in the [Access token scopes](#access-token-scopes) section. 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). + +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) 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. + +[!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 will never 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 + +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 +services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd")); +``` + + 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(); +``` + +### User.Identity.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`). + +To configure the app to receive the value from the `name` claim type: + +* Add a namespace for to `Program.cs`: + + ```csharp + using Microsoft.AspNetCore.Authentication.JwtBearer; + ``` + +* Configure the of the in `Program.cs`: + + ```csharp + builder.Services.Configure( + JwtBearerDefaults.AuthenticationScheme, options => + { + options.TokenValidationParameters.NameClaimType = "name"; + }); + ``` + +### App settings + +The `appsettings.json` file contains the options to configure the JWT bearer handler used to validate access tokens: + +```json +{ + "AzureAd": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "{DOMAIN}", + "TenantId": "{TENANT ID}", + "ClientId": "{SERVER API APP CLIENT ID}", + "CallbackPath": "/signin-oidc" + } +} +``` + +Example: + +```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" + } +} +``` + +[!INCLUDE[](~/blazor/security/includes/azure-scope.md)] + +### WeatherForecast controller + +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. +* The [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) 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. + +```csharp +[Authorize] +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + [HttpGet] + public IEnumerable Get() + { + ... + } +} +``` + +## **:::no-loc text="Client":::** app configuration + +*This section pertains to the solution's **:::no-loc text="Client":::** app.* + +### Authentication package + +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. + +[!INCLUDE[](~/includes/package-reference.md)] + +The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package transitively adds the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +### Authentication service support + +Support for instances is added that include access tokens when making requests to the server project. + +`Program.cs`: + +```csharp +builder.Services.AddHttpClient("{APP ASSEMBLY}.ServerAPI", client => + client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) + .AddHttpMessageHandler(); + +builder.Services.AddScoped(sp => sp.GetRequiredService() + .CreateClient("{APP ASSEMBLY}.ServerAPI")); +``` + +The placeholder `{APP ASSEMBLY}` is the app's assembly name (for example, `BlazorSample.Client`). + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package. This method sets up the services required for the app to interact with the Identity Provider (IP). + +`Program.cs`: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +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. + +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 + +The default access token scopes represent the list of access token scopes that are: + +* Included by default in the sign in request. +* Used to provision an access token immediately after authentication. + +All scopes must belong to the same app per Azure Active Directory rules. Additional scopes can be added for additional API apps as needed: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + ... + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +> [!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 +options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}"); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +### Login mode + +[!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] + +### Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-hosted.md)] + +### Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] + +### App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +### RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +### LoginDisplay component + +[!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] + +### Authentication component + +[!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. + +[!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources diff --git a/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md b/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md index 7c424900c2ff..ef747ea70ff1 100644 --- a/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md +++ b/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md @@ -14,7 +14,7 @@ This article explains how to create a [hosted Blazor WebAssembly solution](xref: For more information on *solutions*, see . -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" > [!IMPORTANT] > [Duende Software](https://duendesoftware.com/) might require you to pay a license fee for production use of Duende Identity Server. For more information, see . @@ -542,7 +542,7 @@ If troubleshooting an Identity Server key-signing certificate loading problem, e Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList ``` -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -1253,7 +1253,7 @@ If troubleshooting an Identity Server key-signing certificate loading problem, e Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList ``` -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -1792,7 +1792,7 @@ If troubleshooting an Identity Server key-signing certificate loading problem, e Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList ``` -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -1805,3 +1805,546 @@ Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Sub * Additional scenarios and use cases, including manual scheme configuration, request path changes for correct request routing, and forwarding the request scheme for Linux and non-IIS reverse proxies. :::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +> [!IMPORTANT] +> [Duende Software](https://duendesoftware.com/) might require you to pay a license fee for production use of Duende Identity Server. For more information, see . + +> [!NOTE] +> To configure a standalone or hosted Blazor WebAssembly app to use an existing, external Identity Server instance, follow the guidance in . + +# [Visual Studio](#tab/visual-studio) + +To create a new Blazor WebAssembly project with an authentication mechanism: + +1. Create a new project. + +1. Choose the **Blazor WebAssembly App** template. Select **Next**. + +1. Provide a **Project name** without using dashes (see the following WARNING). Confirm that the **Location** is correct. Select **Next**. + + > [!WARNING] + > **Avoid using dashes (`-`) in the 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)](https://github.com/dotnet/aspnetcore/issues/35337). + +1. In the **Additional information** dialog, select **Individual Accounts** as the **Authentication Type** to store users within the app using ASP.NET Core's [Identity](xref:security/authentication/identity) system. + +1. Select the **ASP.NET Core hosted** checkbox. + +# [Visual Studio Code / .NET Core CLI](#tab/visual-studio-code+netcore-cli) + +To create a new Blazor WebAssembly project with an authentication mechanism in an empty folder, specify the `Individual` authentication mechanism with the `-au|--auth` option to store users within the app using ASP.NET Core's [Identity](xref:security/authentication/identity) system: + +```dotnetcli +dotnet new blazorwasm -au Individual -ho -o {APP NAME} +``` + +| Placeholder | Example | +| ------------ | -------------- | +| `{APP NAME}` | `BlazorSample` | + +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).** + +For more information, see the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide. + +# [Visual Studio for Mac](#tab/visual-studio-mac) + +To create a new hosted Blazor WebAssembly solution with an authentication mechanism: + +1. Select **Individual Authentication (in-app)** from the **Authentication** dropdown list when creating the app with the **ASP.NET Core hosted** checkbox. For guidance on creating a hosted Blazor WebAssembly solution, see . + +1. The app is created for individual users stored in the app with ASP.NET Core [Identity](xref:security/authentication/identity). + +--- + +## **:::no-loc text="Server":::** app configuration + +The following sections describe additions to the project when authentication support is included. + +### Startup class + +The `Startup` class has the following additions. + +* In `Program.cs`: + + * ASP.NET Core Identity: + + ```csharp + builder.Services.AddDbContext(options => + options.UseSqlite( + Configuration.GetConnectionString("DefaultConnection"))); + + builder.Services.AddDefaultIdentity(options => + options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores(); + ``` + + * IdentityServer with an additional helper method that sets up default ASP.NET Core conventions on top of IdentityServer: + + ```csharp + builder.Services.AddIdentityServer() + .AddApiAuthorization(); + ``` + + * Authentication with an additional helper method that configures the app to validate JWT tokens produced by IdentityServer: + + ```csharp + builder.Services.AddAuthentication() + .AddIdentityServerJwt(); + ``` + +* In `Program.cs`: + + * The IdentityServer middleware exposes the OpenID Connect (OIDC) endpoints: + + ```csharp + app.UseIdentityServer(); + ``` + + * The Authentication middleware is responsible for validating request credentials and setting the user on the request context: + + ```csharp + app.UseAuthentication(); + ``` + + * Authorization Middleware enables authorization capabilities: + + ```csharp + app.UseAuthorization(); + ``` + +### Azure App Service on Linux + +Specify the issuer explicitly when deploying to Azure App Service on Linux. For more information, see . + +### AddApiAuthorization + +The helper method configures [Identity Server](https://docs.duendesoftware.com) for ASP.NET Core scenarios. Identity Server is a powerful and extensible framework for handling app security concerns. IdentityServer exposes unnecessary complexity for the most common scenarios. Consequently, a set of conventions and configuration options is provided that we consider a good starting point. Once your authentication needs change, the full power of IdentityServer is available to customize authentication to suit an app's requirements. + +### AddIdentityServerJwt + +The helper method configures a policy scheme for the app as the default authentication handler. The policy is configured to allow Identity to handle all requests routed to any subpath in the Identity URL space `/Identity`. The handles all other requests. Additionally, this method: + +* Registers an `{APPLICATION NAME}API` API resource with IdentityServer with a default scope of `{APPLICATION NAME}API`. +* Configures the JWT Bearer Token Middleware to validate tokens issued by IdentityServer for the app. + +### WeatherForecastController + +In the `WeatherForecastController` (`Controllers/WeatherForecastController.cs`), the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) is applied to the class. The attribute indicates that the user must be authorized based on the default policy to access the resource. The default authorization policy is configured to use the default authentication scheme, which is set up by . The helper method configures as the default handler for requests to the app. + +### ApplicationDbContext + +In the `ApplicationDbContext` (`Data/ApplicationDbContext.cs`), extends to include the schema for IdentityServer. is derived from . + +To gain full control of the database schema, inherit from one of the available Identity classes and configure the context to include the Identity schema by calling `builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)` in the method. + +### OidcConfigurationController + +In the `OidcConfigurationController` (`Controllers/OidcConfigurationController.cs`), the client endpoint is provisioned to serve OIDC parameters. + +### App settings + +In the app settings file (`appsettings.json`) at the project root, the `IdentityServer` section describes the list of configured clients. In the following example, there's a single client. The client name corresponds to the app name and is mapped by convention to the OAuth `ClientId` parameter. The profile indicates the app type being configured. The profile is used internally to drive conventions that simplify the configuration process for the server. + +```json +"IdentityServer": { + "Clients": { + "{APP ASSEMBLY}.Client": { + "Profile": "IdentityServerSPA" + } + } +} +``` + +The placeholder `{APP ASSEMBLY}` is the app's assembly name (for example, `BlazorSample.Client`). + +## **:::no-loc text="Client":::** app configuration + +### Authentication package + +When an app is created to use Individual User Accounts (`Individual`), the app automatically receives a package reference for the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package. 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.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +[!INCLUDE[](~/includes/package-reference.md)] + +### `HttpClient` configuration + +In `Program.cs`, a named (`{APP ASSEMBLY}.ServerAPI`) is configured to supply instances that include access tokens when making requests to the server API: + +```csharp +builder.Services.AddHttpClient("{APP ASSEMBLY}.ServerAPI", + client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) + .AddHttpMessageHandler(); + +builder.Services.AddScoped(sp => sp.GetRequiredService() + .CreateClient("{APP ASSEMBLY}.ServerAPI")); +``` + +The placeholder `{APP ASSEMBLY}` is the app's assembly name (for example, `BlazorSample.Client`). + +> [!NOTE] +> If you're configuring a Blazor WebAssembly app to use an existing Identity Server instance that isn't part of a hosted Blazor solution, change the base address registration from (`builder.HostEnvironment.BaseAddress`) to the server app's API authorization endpoint URL. + +### API authorization support + +The support for authenticating users is plugged into the service container by the extension method provided inside the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package. This method sets up the services required by the app to interact with the existing authorization system. + +```csharp +builder.Services.AddApiAuthorization(); +``` + +By default, configuration for the app is loaded by convention from `_configuration/{client-id}`. By convention, the client ID is set to the app's assembly name. This URL can be changed to point to a separate endpoint by calling the overload with options. + +### Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-hosted.md)] + +### Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-authentication.md)] + +### App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +### RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +### LoginDisplay component + +The `LoginDisplay` component (`Shared/LoginDisplay.razor`) is rendered in the `MainLayout` component (`Shared/MainLayout.razor`) and manages the following behaviors: + +* For authenticated users: + * Displays the current user name. + * Offers a link to the user profile page in ASP.NET Core Identity. + * Offers a button to log out of the app. +* For anonymous users: + * Offers the option to register. + * Offers the option to log in. + +Due to changes in the framework across releases of ASP.NET Core, Razor markup for the `LoginDisplay` component 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 `LoginDisplay` component in the generated app. +* Inspect the `LoginDisplay` component in [reference source](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/LoginDisplay.IndividualLocalAuth.razor). The templated content for `Hosted` equal to `true` is used. + + [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +### Authentication component + +[!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. + +## Name and role claim with API authorization + +### Custom user factory + +In the **:::no-loc text="Client":::** app, create a custom user factory. Identity Server sends multiple roles as a JSON array in a single `role` claim. A single role is sent as a string value in the claim. The factory creates an individual `role` claim for each of the user's roles. + +`CustomUserFactory.cs`: + +```csharp +using System.Linq; +using System.Security.Claims; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; + +public class CustomUserFactory + : AccountClaimsPrincipalFactory +{ + public CustomUserFactory(IAccessTokenProviderAccessor accessor) + : base(accessor) + { + } + + public override async ValueTask CreateUserAsync( + RemoteUserAccount account, + RemoteAuthenticationUserOptions options) + { + var user = await base.CreateUserAsync(account, options); + + if (user.Identity.IsAuthenticated) + { + var identity = (ClaimsIdentity)user.Identity; + var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray(); + + if (roleClaims.Any()) + { + foreach (var existingClaim in roleClaims) + { + identity.RemoveClaim(existingClaim); + } + + var rolesElem = account.AdditionalProperties[identity.RoleClaimType]; + + if (rolesElem is JsonElement roles) + { + if (roles.ValueKind == JsonValueKind.Array) + { + foreach (var role in roles.EnumerateArray()) + { + identity.AddClaim(new Claim(options.RoleClaim, role.GetString())); + } + } + else + { + identity.AddClaim(new Claim(options.RoleClaim, roles.GetString())); + } + } + } + } + + return user; + } +} +``` + +In the **:::no-loc text="Client":::** app, register the factory in `Program.cs`: + +```csharp +builder.Services.AddApiAuthorization() + .AddAccountClaimsPrincipalFactory(); +``` + +In the **:::no-loc text="Server":::** app, call on the Identity builder, which adds role-related services: + +```csharp +using Microsoft.AspNetCore.Identity; + +... + +services.AddDefaultIdentity(options => + options.SignIn.RequireConfirmedAccount = true) + .AddRoles() + .AddEntityFrameworkStores(); +``` + +### Configure Identity Server + +Use **one** of the following approaches: + +* [API authorization options](#api-authorization-options) +* [Profile Service](#profile-service) + +#### API authorization options + +In the **:::no-loc text="Server":::** app: + +* Configure Identity Server to put the `name` and `role` claims into the ID token and access token. +* Prevent the default mapping for roles in the JWT token handler. + +```csharp +using System.IdentityModel.Tokens.Jwt; +using System.Linq; + +... + +services.AddIdentityServer() + .AddApiAuthorization(options => { + options.IdentityResources["openid"].UserClaims.Add("name"); + options.ApiResources.Single().UserClaims.Add("name"); + options.IdentityResources["openid"].UserClaims.Add("role"); + options.ApiResources.Single().UserClaims.Add("role"); + }); + +JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role"); +``` + +#### Profile Service + +In the **:::no-loc text="Server":::** app, create a `ProfileService` implementation. + +`ProfileService.cs`: + +```csharp +using IdentityModel; +using Duende.IdentityServer.Models; +using Duende.IdentityServer.Services; +using System.Threading.Tasks; + +public class ProfileService : IProfileService +{ + public ProfileService() + { + } + + public async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name); + context.IssuedClaims.AddRange(nameClaim); + + var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role); + context.IssuedClaims.AddRange(roleClaims); + + await Task.CompletedTask; + } + + public async Task IsActiveAsync(IsActiveContext context) + { + await Task.CompletedTask; + } +} +``` + +In the **:::no-loc text="Server":::** app, register the Profile Service in `Program.cs`: + +```csharp +using Duende.IdentityServer.Services; + +... + +builder.Services.AddTransient(); + +JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role"); +``` + +### Use authorization mechanisms + +In the **:::no-loc text="Client":::** app, component authorization approaches are functional at this point. Any of the authorization mechanisms in components can use a role to authorize the user: + +* [`AuthorizeView` component](xref:blazor/security/index#authorizeview-component) (Example: ``) +* [`[Authorize]` attribute directive](xref:blazor/security/index#authorize-attribute) () (Example: `@attribute [Authorize(Roles = "admin")]`) +* [Procedural logic](xref:blazor/security/index#procedural-logic) (Example: `if (user.IsInRole("admin")) { ... }`) + + Multiple role tests are supported: + + ```csharp + if (user.IsInRole("admin") && user.IsInRole("developer")) + { + ... + } + ``` + +`User.Identity.Name` is populated in the **:::no-loc text="Client":::** app with the user's user name, which is usually their sign-in email address. + +[!INCLUDE[](~/blazor/security/includes/usermanager-signinmanager.md)] + +## Host in Azure App Service with a custom domain and certificate + +The following guidance explains: + +* How to deploy a hosted Blazor WebAssembly app with Identity Server to [Azure App Service](https://azure.microsoft.com/services/app-service/) with a custom domain. +* How to create and use a TLS certificate for HTTPS protocol communication with browsers. Although the guidance focuses on using the certificate with a custom domain, the guidance is equally applicable to using a default Azure Apps domain, for example `contoso.azurewebsites.net`. + +For this hosting scenario, do **not** use the same certificate for [Identity Server's token signing key](https://docs.duendesoftware.com/identityserver/v5/fundamentals/keys/) and the site's HTTPS secure communication with browsers: + +* Using different certificates for these two requirements is a good security practice because it isolates private keys for each purpose. +* TLS certificates for communication with browsers is managed independently without affecting Identity Server's token signing. +* When [Azure Key Vault](https://azure.microsoft.com/services/key-vault/) supplies a certificate to an App Service app for custom domain binding, Identity Server can't obtain the same certificate from Azure Key Vault for token signing. Although configuring Identity Server to use the same TLS certificate from a physical path is possible, placing security certificates into source control is a **poor practice and should be avoided in most scenarios**. + +In the following guidance, a self-signed certificate is created in Azure Key Vault solely for Identity Server token signing. The Identity Server configuration uses the key vault certificate via the app's `CurrentUser` > `My` certificate store. Other certificates used for HTTPS traffic with custom domains are created and configured separately from the Identity Server signing certificate. + +To configure an app, Azure App Service, and Azure Key Vault to host with a custom domain and HTTPS: + +1. Create an [App Service plan](/azure/app-service/overview-hosting-plans) with an plan level of `Basic B1` or higher. App Service requires a `Basic B1` or higher service tier to use custom domains. +1. Create a PFX certificate for the site's secure browser communication (HTTPS protocol) with a common name of the site's fully qualified domain name (FQDN) that your organization controls (for example, `www.contoso.com`). Create the certificate with: + * Key uses + * Digital signature validation (`digitalSignature`) + * Key encipherment (`keyEncipherment`) + * Enhanced/extended key uses + * Client Authentication (1.3.6.1.5.5.7.3.2) + * Server Authentication (1.3.6.1.5.5.7.3.1) + + To create the certificate, use one of the following approaches or any other suitable tool or online service: + + * [Azure Key Vault](/azure/key-vault/certificates/quick-create-portal#add-a-certificate-to-key-vault) + * [MakeCert on Windows](/windows/desktop/seccrypto/makecert) + * [OpenSSL](https://www.openssl.org) + + Make note of the password, which is used later to import the certificate into Azure Key Vault. + + For more information on Azure Key Vault certificates, see [Azure Key Vault: Certificates](/azure/key-vault/certificates/). +1. Create a new Azure Key Vault or use an existing key vault in your Azure subscription. +1. In the key vault's **Certificates** area, import the PFX site certificate. Record the certificate's thumbprint, which is used in the app's configuration later. +1. In Azure Key Vault, generate a new self-signed certificate for Identity Server token signing. Give the certificate a **Certificate Name** and **Subject**. The **Subject** is specified as `CN={COMMON NAME}`, where the `{COMMON NAME}` placeholder is the certificate's common name. The common name can be any alphanumeric string. For example, `CN=IdentityServerSigning` is a valid certificate **Subject**. In **Issuance Policy** > **Advanced Policy Configuration**, use the default settings. Record the certificate's thumbprint, which is used in the app's configuration later. +1. Navigate to Azure App Service in the Azure portal and create a new App Service with the following configuration: + * **Publish** set to `Code`. + * **Runtime stack** set to the app's runtime. + * For **Sku and size**, confirm that the App Service tier is `Basic B1` or higher. App Service requires a `Basic B1` or higher service tier to use custom domains. +1. After Azure creates the App Service, open the app's **Configuration** and add a new application setting specifying the certificate thumbprints recorded earlier. The app setting key is `WEBSITE_LOAD_CERTIFICATES`. Separate the certificate thumbprints in the app setting value with a comma, as the following example shows: + * Key: `WEBSITE_LOAD_CERTIFICATES` + * Value: `57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1` + + In the Azure portal, saving app settings is a two-step process: Save the `WEBSITE_LOAD_CERTIFICATES` key-value setting, then select the **Save** button at the top of the blade. +1. Select the app's **TLS/SSL settings**. Select **Private Key Certificates (.pfx)**. Use the **Import Key Vault Certificate** process. **Use the process _twice_ to import both the site's certificate for HTTPS communication and the site's self-signed Identity Server token signing certificate.** +1. Navigate to the **Custom domains** blade. At your domain registrar's website, use the **IP address** and **Custom Domain Verification ID** to configure the domain. A typical domain configuration includes: + * An **A Record** with a **Host** of `@` and a value of the IP address from the Azure portal. + * A **TXT Record** with a **Host** of `asuid` and the value of the verification ID generated by Azure and provided by the Azure portal. + + Make sure that you save the changes at your domain registrar's website correctly. Some registrar websites require a two-step process to save domain records: One or more records are saved individually followed by updating the domain's registration with a separate button. +1. Return to the **Custom domains** blade in the Azure portal. Select **Add custom domain**. Select the **A Record** option. Provide the domain and select **Validate**. If the domain records are correct and propagated across the Internet, the portal allows you to select the **Add custom domain** button. + + It can take a few days for domain registration changes to propagate across Internet domain name servers (DNS) after they're processed by your domain registrar. If domain records aren't updated within three business days, confirm the records are correctly set with the domain registrar and contact their customer support. +1. In the **Custom domains** blade, the **SSL STATE** for the domain is marked `Not Secure`. Select the **Add binding** link. Select the site HTTPS certificate from the key vault for the custom domain binding. +1. In Visual Studio, open the *Server* project's app settings file (`appsettings.json` or `appsettings.Production.json`). In the Identity Server configuration, add the following `Key` section. Specify the self-signed certificate **Subject** for the `Name` key. In the following example, the certificate's common name assigned in the key vault is `IdentityServerSigning`, which yields a **Subject** of `CN=IdentityServerSigning`: + + ```json + "IdentityServer": { + + ... + + "Key": { + "Type": "Store", + "StoreName": "My", + "StoreLocation": "CurrentUser", + "Name": "CN=IdentityServerSigning" + } + }, + ``` + +1. In Visual Studio, create an Azure App Service [publish profile](xref:host-and-deploy/visual-studio-publish-profiles#publish-profiles) for the *Server* project. From the menu bar, select: **Build** > **Publish** > **New** > **Azure** > **Azure App Service** (Windows or Linux). When Visual Studio is connected to an Azure subscription, you can set the **View** of Azure resources by **Resource type**. Navigate within the **Web App** list to find the App Service for the app and select it. Select **Finish**. +1. When Visual Studio returns to the **Publish** window, the key vault and SQL Server database service dependencies are automatically detected. + + No configuration changes to the default settings are required for the key vault service. + + For testing purposes, an app's local [SQLite](https://www.sqlite.org/index.html) database, which is configured by default by the Blazor template, can be deployed with the app without additional configuration. Configuring a different database for Identity Server in production is beyond the scope of this article. For more information, see the database resources in the following documentation sets: + * [App Service](/azure/app-service/) + * [Duende Identity Server](https://docs.duendesoftware.com) + +1. Select the **Edit** link under the deployment profile name at the top of the window. Change the destination URL to the site's custom domain URL (for example, `https://www.contoso.com`). Save the settings. +1. Publish the app. Visual Studio opens a browser window and requests the site at its custom domain. + +The Azure documentation contains additional detail on using Azure services and custom domains with TLS binding in App Service, including information on using CNAME records instead of A records. For more information, see the following resources: + +* [App Service documentation](/azure/app-service/) +* [Tutorial: Map an existing custom DNS name to Azure App Service](/azure/app-service/app-service-web-tutorial-custom-domain) +* [Secure a custom DNS name with a TLS/SSL binding in Azure App Service](/azure/app-service/configure-ssl-bindings) +* [Azure Key Vault](/azure/key-vault/) + +We recommend using a new in-private or incognito browser window for each app test run after a change to the app, app configuration, or Azure services in the Azure portal. Lingering cookies from a previous test run can result in failed authentication or authorization when testing the site even when the site's configuration is correct. For more information on how to configure Visual Studio to open a new in-private or incognito browser window for each test run, see the [Cookies and site data](#cookies-and-site-data) section. + +When App Service configuration is changed in the Azure portal, the updates generally take effect quickly but aren't instant. Sometimes, you must wait a short period for an App Service to restart in order for a configuration change to take effect. + +If troubleshooting an Identity Server key-signing certificate loading problem, execute the following command in an Azure portal [Kudu](https://github.com/projectkudu/kudu/wiki/Accessing-the-kudu-service) PowerShell command shell. The command provides a list of certificates that the app can access from the `CurrentUser` > `My` certificate store. The output includes certificate subjects and thumbprints useful when debugging an app: + +```powershell +Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList +``` + +[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] + +## Additional resources + +* [Deployment to Azure App Service](xref:security/authentication/identity/spa#deploy-to-production) +* [Import a certificate from Key Vault (Azure documentation)](/azure/app-service/configure-ssl-certificate#import-a-certificate-from-key-vault) +* +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* : Includes guidance on: + * Using Forwarded Headers Middleware to preserve HTTPS scheme information across proxy servers and internal networks. + * Additional scenarios and use cases, including manual scheme configuration, request path changes for correct request routing, and forwarding the request scheme for Linux and non-IIS reverse proxies. +* [Duende Identity Server](https://docs.duendesoftware.com) + +:::moniker-end diff --git a/aspnetcore/blazor/security/webassembly/index.md b/aspnetcore/blazor/security/webassembly/index.md index 3f888a2a876b..1c094657b233 100644 --- a/aspnetcore/blazor/security/webassembly/index.md +++ b/aspnetcore/blazor/security/webassembly/index.md @@ -677,6 +677,10 @@ To secure a SignalR hub: For more information, see . +## Logging + +To enable debug or trace logging for Blazor WebAssembly authentication, see . + ## Implementation guidance Articles under this *Overview* provide information on authenticating users in Blazor WebAssembly apps against specific providers. diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md b/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md index 5b613809ed17..bdf410c87d1d 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-authentication-library.md @@ -12,7 +12,7 @@ uid: blazor/security/webassembly/standalone-with-authentication-library This article explains how to secure an ASP.NET Core Blazor WebAssembly standalone app with the Blazor WebAssembly Authentication library. -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" *For Azure Active Directory (AAD) and Azure Active Directory B2C (AAD B2C), don't follow the guidance in this topic. See the AAD and AAD B2C topics in this table of contents node.* @@ -164,7 +164,7 @@ Due to changes in the framework across releases of ASP.NET Core, Razor markup fo [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -328,7 +328,7 @@ Due to changes in the framework across releases of ASP.NET Core, Razor markup fo [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -492,6 +492,170 @@ Due to changes in the framework across releases of ASP.NET Core, Razor markup fo [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* : Includes guidance on: + * Using Forwarded Headers Middleware to preserve HTTPS scheme information across proxy servers and internal networks. + * Additional scenarios and use cases, including manual scheme configuration, request path changes for correct request routing, and forwarding the request scheme for Linux and non-IIS reverse proxies. + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +*For Azure Active Directory (AAD) and Azure Active Directory B2C (AAD B2C), don't follow the guidance in this topic. See the AAD and AAD B2C topics in this table of contents node.* + +To create a [standalone Blazor WebAssembly app](xref:blazor/hosting-models#blazor-webassembly) that uses [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) library, follow the guidance for your choice of tooling. If adding support for authentication, see the following sections of this article for guidance on setting up and configuring the app. + +> [!NOTE] +> The Identity Provider (IP) must use [OpenID Connect (OIDC)](https://openid.net/connect/). For example, Facebook's IP isn't an OIDC-compliant provider, so the guidance in this topic doesn't work with the Facebook IP. For more information, see . + +# [Visual Studio](#tab/visual-studio) + +To create a new Blazor WebAssembly project with an authentication mechanism: + +1. After choosing the **Blazor WebAssembly App** template in the **Create a new ASP.NET Core Web Application** dialog, select **Change** under **Authentication**. + +1. Select **Individual User Accounts** to use ASP.NET Core's [Identity](xref:security/authentication/identity) system. This selection adds authentication support and doesn't result in storing users in a database. The following sections of this article provide further details. + +# [Visual Studio Code / .NET Core CLI](#tab/visual-studio-code+netcore-cli) + +Create a new Blazor WebAssembly project with an authentication mechanism in an empty folder. Specify the `Individual` authentication mechanism with the `-au|--auth` option to use ASP.NET Core's [Identity](xref:security/authentication/identity) system. This selection adds authentication support and doesn't result in storing users in a database. The following sections of this article provide further details. + +```dotnetcli +dotnet new blazorwasm -au Individual -o {APP NAME} +``` + +| Placeholder | Example | +| ------------ | -------------- | +| `{APP NAME}` | `BlazorSample` | + +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. + +For more information, see the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide. + +# [Visual Studio for Mac](#tab/visual-studio-mac) + +To create a new Blazor WebAssembly project with an authentication mechanism: + +1. Select **Individual Authentication (in-app)** from the **Authentication** dropdown list when creating the app. For guidance on creating a Blazor WebAssembly app, see . + +1. The app is created to use ASP.NET Core [Identity](xref:security/authentication/identity) and doesn't result in storing users in a database. The following sections of this article provide further details. + +--- + +## Authentication package + +When an app is created to use Individual User Accounts, the app automatically receives a package reference for the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package. 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.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +[!INCLUDE[](~/includes/package-reference.md)] + +## Authentication service support + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package. This method sets up the services required for the app to interact with the Identity Provider (IP). + +For a new app, provide values for the `{AUTHORITY}` and `{CLIENT ID}` placeholders in the following configuration. Provide other configuration values that are required for use with the app's IP. The example is for Google, which requires `PostLogoutRedirectUri`, `RedirectUri`, and `ResponseType`. If adding authentication to an app, manually add the following code and configuration to the app with values for the placeholders and other configuration values. + +`Program.cs`: + +```csharp +builder.Services.AddOidcAuthentication(options => +{ + builder.Configuration.Bind("Local", options.ProviderOptions); +}); +``` + +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "Local": { + "Authority": "{AUTHORITY}", + "ClientId": "{CLIENT ID}" + } +} +``` + +Google OAuth 2.0 OIDC example for an app that runs on the localhost address at port 5001: + +```json +{ + "Local": { + "Authority": "https://accounts.google.com/", + "ClientId": "2.......7-e.....................q.apps.googleusercontent.com", + "PostLogoutRedirectUri": "https://localhost:5001/authentication/logout-callback", + "RedirectUri": "https://localhost:5001/authentication/login-callback", + "ResponseType": "id_token" + } +} +``` + +The redirect URI (`https://localhost:5001/authentication/login-callback`) is registered in the [Google APIs console](https://console.developers.google.com/apis/dashboard) in **Credentials** > **`{NAME}`** > **Authorized redirect URIs**, where `{NAME}` is the app's client name in the **OAuth 2.0 Client IDs** app list of the Google APIs console. + +Authentication support for standalone apps is offered using OpenID Connect (OIDC). The method accepts a callback to configure the parameters required to authenticate an app using OIDC. The values required for configuring the app can be obtained from the OIDC-compliant IP. Obtain the values when you register the app, which typically occurs in their online portal. + +## Access token scopes + +The Blazor WebAssembly template automatically configures default scopes for `openid` and `profile`. + +The Blazor WebAssembly template doesn't automatically configure the app to request an access token for a secure API. To provision an access token as part of the sign-in flow, add the scope to the default token scopes of the . If adding authentication to an app, manually add the following code and configure the scope URI. + +`Program.cs`: + +```csharp +builder.Services.AddOidcAuthentication(options => +{ + ... + options.ProviderOptions.DefaultScopes.Add("{SCOPE URI}"); +}); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +## Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-standalone.md)] + +## Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-authentication.md)] + +## App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +## RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +## LoginDisplay component + +The `LoginDisplay` component (`Shared/LoginDisplay.razor`) is rendered in the `MainLayout` component (`Shared/MainLayout.razor`) and manages the following behaviors: + +* For authenticated users: + * Displays the current username. + * Offers a button to log out of the app. +* For anonymous users, offers the option to log in. + +Due to changes in the framework across releases of ASP.NET Core, Razor markup for the `LoginDisplay` component 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 `LoginDisplay` component in the generated app. +* Inspect the `LoginDisplay` component in [reference source](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Shared/LoginDisplay.IndividualMsalAuth.razor). + + [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +## Authentication component + +[!INCLUDE[](~/blazor/security/includes/authentication-component.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory-b2c.md b/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory-b2c.md index e4f0e5a3a314..8534de6d0c8f 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory-b2c.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory-b2c.md @@ -12,7 +12,7 @@ uid: blazor/security/webassembly/standalone-with-azure-active-directory-b2c This article explains how to create a [standalone Blazor WebAssembly app](xref:blazor/hosting-models#blazor-webassembly) that uses [Azure Active Directory (AAD) B2C](/azure/active-directory-b2c/overview) for authentication. -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Create a tenant or identify an existing B2C tenant for the app to use in the Azure portal by following the guidance in the [Create an AAD B2C tenant (Azure documentation)](/azure/active-directory-b2c/tutorial-create-tenant) article. Return to this article immediately after creating or identifying a tenant to use. @@ -179,7 +179,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -358,7 +358,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -531,6 +531,187 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library) +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* +* [Tutorial: Create an Azure Active Directory B2C tenant](/azure/active-directory-b2c/tutorial-create-tenant) +* [Tutorial: Register an application in Azure Active Directory B2C](/azure/active-directory-b2c/tutorial-register-applications) +* [Microsoft identity platform documentation](/azure/active-directory/develop/) + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +Create a tenant or identify an existing B2C tenant for the app to use in the Azure portal by following the guidance in the [Create an AAD B2C tenant (Azure documentation)](/azure/active-directory-b2c/tutorial-create-tenant) article. Return to this article immediately after creating or identifying a tenant to use. + +Record the following information: + +* AAD B2C instance (for example, `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. +* AAD B2C 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. + +Register an AAD B2C app: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Standalone AAD B2C**). +1. For **Supported account types**, select the multi-tenant option: **Accounts in any organizational directory or any identity provider. For authenticating users with Azure AD B2C.** +1. Set the **Redirect URI** drop down to **Single-page application (SPA)** and provide the following redirect URI: `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. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), confirm that **Permissions** > **Grant admin consent to openid and offline_access permissions** is selected. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +> [!NOTE] +> Supplying the port number for a `localhost` AAD B2C redirect URI isn't required. For more information, see [Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Azure documentation)](/azure/active-directory/develop/reply-url#localhost-exceptions). + +Record the Application (client) ID (for example, `41451fa7-82d9-4673-8fa5-69eff5a761fd`). + +In **Authentication** > **Platform configurations** > **Single-page application (SPA)**: + +1. Confirm the **Redirect URI** of `https://localhost/authentication/login-callback` is present. +1. In the **Implicit grant** section, ensure that the checkboxes for **Access tokens** and **ID tokens** are **not** selected. +1. The remaining defaults for the app are acceptable for this experience. +1. Select the **Save** button. + +In **Home** > **Azure AD B2C** > **User flows**: + +[Create a sign-up and sign-in user flow](/azure/active-directory-b2c/tutorial-create-user-flows) + +At a minimum, select the **Application claims** > **Display Name** user attribute to populate the `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_signupsignin`). + +In an empty folder, replace the placeholders in the following command with the information recorded earlier and execute the command in a command shell: + +```dotnetcli +dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --client-id "{CLIENT ID}" --domain "{TENANT DOMAIN}" -o {APP NAME} -ssp "{SIGN UP OR SIGN IN POLICY}" +``` + +| Placeholder | Azure portal name | Example | +| ----------------------------- | ------------------------------- | ------------------------------------------------------------- | +| `{AAD B2C INSTANCE}` | Instance | `https://contoso.b2clogin.com/` (includes the trailing slash) | +| `{APP NAME}` | — | `BlazorSample` | +| `{CLIENT ID}` | Application (client) ID | `41451fa7-82d9-4673-8fa5-69eff5a761fd` | +| `{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 app's name. + +[!INCLUDE[](~/blazor/security/includes/additional-scopes-standalone-nonAAD.md)] + +After creating the app, you should be able to: + +* Log into the app using an AAD user account. +* Request access tokens for Microsoft APIs. For more information, see: + * [Access token scopes](#access-token-scopes) + * [Quickstart: Configure an application to expose web APIs](/azure/active-directory/develop/quickstart-configure-app-expose-web-apis). + +## Authentication package + +When an app is created to use an Individual B2C Account (`IndividualB2C`), 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. + +[!INCLUDE[](~/includes/package-reference.md)] + +The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package transitively adds the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +## Authentication service support + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package. This method sets up all of the services required for the app to interact with the Identity Provider (IP). + +`Program.cs`: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); +}); +``` + +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 AAD configuration when you register the app. + +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "AzureAdB2C": { + "Authority": "{AAD B2C INSTANCE}{DOMAIN}/{SIGN UP OR SIGN IN POLICY}", + "ClientId": "{CLIENT ID}", + "ValidateAuthority": false + } +} +``` + +In the preceding configuration, the `{AAD B2C INSTANCE}` includes a trailing slash. + +Example: + +```json +{ + "AzureAdB2C": { + "Authority": "https://contoso.b2clogin.com/contoso.onmicrosoft.com/B2C_1_signupsignin1", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "ValidateAuthority": false + } +} +``` + +## Access token scopes + +The Blazor WebAssembly template doesn't automatically configure the app to request an access token for a secure API. To provision an access token as part of the sign-in flow, add the scope to the default access token scopes of the : + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + ... + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +Specify additional scopes with `AdditionalScopesToConsent`: + +```csharp +options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}"); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +## Login mode + +[!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] + +## Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-standalone.md)] + +## Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] + +## App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +## RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +## LoginDisplay component + +[!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] + +## Authentication component + +[!INCLUDE[](~/blazor/security/includes/authentication-component.md)] + +[!INCLUDE[](~/blazor/security/includes/wasm-aad-b2c-userflows.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory.md b/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory.md index fe3665c81ea1..6cdf931f3f4e 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-azure-active-directory.md @@ -12,7 +12,7 @@ uid: blazor/security/webassembly/standalone-with-azure-active-directory This article explains how to create a [standalone Blazor WebAssembly app](xref:blazor/hosting-models#blazor-webassembly) that uses [Azure Active Directory (AAD)](https://azure.microsoft.com/services/active-directory/) for authentication. -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Register an AAD app: @@ -163,7 +163,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -327,7 +327,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -487,6 +487,170 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library) +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* +* +* [Microsoft identity platform documentation](/azure/active-directory/develop/) + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +Register an AAD app: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Standalone AAD**). +1. Choose a **Supported account types**. You may select **Accounts in this organizational directory only** for this experience. +1. Set the **Redirect URI** drop down to **Single-page application (SPA)** and provide the following redirect URI: `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. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), clear the **Permissions** > **Grant admin consent to openid and offline_access permissions** checkbox. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +> [!NOTE] +> Supplying the port number for a `localhost` AAD redirect URI isn't required. For more information, see [Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Azure documentation)](/azure/active-directory/develop/reply-url#localhost-exceptions). + +Record the following information: + +* Application (client) ID (for example, `41451fa7-82d9-4673-8fa5-69eff5a761fd`) +* Directory (tenant) ID (for example, `e86c78e2-8bb4-4c41-aefd-918e0565a45e`) + +In **Authentication** > **Platform configurations** > **Single-page application (SPA)**: + +1. Confirm the **Redirect URI** of `https://localhost/authentication/login-callback` is present. +1. In the **Implicit grant** section, ensure that the checkboxes for **Access tokens** and **ID tokens** are **not** selected. +1. The remaining defaults for the app are acceptable for this experience. +1. Select the **Save** button. + +Create the 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: + +```dotnetcli +dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" -o {APP NAME} --tenant-id "{TENANT ID}" +``` + +| Placeholder | Azure portal name | Example | +| ------------- | ----------------------- | -------------------------------------- | +| `{APP NAME}` | — | `BlazorSample` | +| `{CLIENT ID}` | Application (client) ID | `41451fa7-82d9-4673-8fa5-69eff5a761fd` | +| `{TENANT ID}` | Directory (tenant) ID | `e86c78e2-8bb4-4c41-aefd-918e0565a45e` | + +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. + +[!INCLUDE[](~/blazor/security/includes/additional-scopes-standalone-AAD.md)] + +After creating the app, you should be able to: + +* Log into the app using an AAD user account. +* Request access tokens for Microsoft APIs. For more information, see: + * [Access token scopes](#access-token-scopes) + * [Quickstart: Configure an application to expose web APIs](/azure/active-directory/develop/quickstart-configure-app-expose-web-apis) + * [Hosted with AAD: Access token scopes (includes guidance on AAD `App ID URI` scope formats)](xref:blazor/security/webassembly/hosted-with-azure-active-directory#access-token-scopes) + * [Access token scopes for Microsoft Graph API](xref:blazor/security/webassembly/graph-api) + +## Authentication package + +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. + +[!INCLUDE[](~/includes/package-reference.md)] + +The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package transitively adds the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +## Authentication service support + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package. This method sets up the services required for the app to interact with the Identity Provider (IP). + +`Program.cs`: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); +}); +``` + +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 AAD configuration when you register the app. + +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/{TENANT ID}", + "ClientId": "{CLIENT ID}", + "ValidateAuthority": true + } +} +``` + +Example: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/e86c78e2-...-918e0565a45e", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "ValidateAuthority": true + } +} +``` + +## Access token scopes + +The Blazor WebAssembly template doesn't automatically configure the app to request an access token for a secure API. To provision an access token as part of the sign-in flow, add the scope to the default access token scopes of the : + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + ... + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +Specify additional scopes with `AdditionalScopesToConsent`: + +```csharp +options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}"); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +## Login mode + +[!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] + +## Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-standalone.md)] + +## Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] + +## App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +## RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +## LoginDisplay component + +[!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] + +## Authentication component + +[!INCLUDE[](~/blazor/security/includes/authentication-component.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-microsoft-accounts.md b/aspnetcore/blazor/security/webassembly/standalone-with-microsoft-accounts.md index f5a5f2247385..7132716cd8cd 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-microsoft-accounts.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-microsoft-accounts.md @@ -12,7 +12,7 @@ uid: blazor/security/webassembly/standalone-with-microsoft-accounts This article explains how to create a [standalone Blazor WebAssembly app](xref:blazor/hosting-models#blazor-webassembly) that uses [Microsoft Accounts with Azure Active Directory (AAD)](/azure/active-directory/develop/quickstart-register-app#register-a-new-application-using-the-azure-portal) for authentication. -:::moniker range=">= aspnetcore-6.0" +:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0" Register an AAD app: @@ -157,7 +157,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -315,7 +315,7 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] -[!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] ## Additional resources @@ -469,6 +469,164 @@ For more information, see the following sections of the *Additional scenarios* a [!INCLUDE[](~/blazor/security/includes/authentication-component.md)] +[!INCLUDE[](~/blazor/security/includes/troubleshoot-60-or-earlier.md)] + +## Additional resources + +* +* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library) +* [Unauthenticated or unauthorized web API requests in an app with a secure default client](xref:blazor/security/webassembly/additional-scenarios#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client) +* +* [Quickstart: Register an application with the Microsoft identity platform](/azure/active-directory/develop/quickstart-register-app#register-a-new-application-using-the-azure-portal) +* [Quickstart: Configure an application to expose web APIs](/azure/active-directory/develop/quickstart-configure-app-expose-web-apis) + +:::moniker-end + +:::moniker range=">= aspnetcore-7.0" + +Register an AAD app: + +1. Navigate to **Azure Active Directory** in the Azure portal. Select **App registrations** in the sidebar. Select the **New registration** button. +1. Provide a **Name** for the app (for example, **Blazor Standalone AAD Microsoft Accounts**). +1. In **Supported account types**, select **Accounts in any organizational directory**. +1. Set the **Redirect URI** drop down to **Single-page application (SPA)** and provide the following redirect URI: `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. +1. If you're using an [unverified publisher domain](/azure/active-directory/develop/howto-configure-publisher-domain), clear the **Permissions** > **Grant admin consent to openid and offline_access permissions** checkbox. If the publisher domain is verified, this checkbox isn't present. +1. Select **Register**. + +> [!NOTE] +> Supplying the port number for a `localhost` AAD redirect URI isn't required. For more information, see [Redirect URI (reply URL) restrictions and limitations: Localhost exceptions (Azure documentation)](/azure/active-directory/develop/reply-url#localhost-exceptions). + +Record the Application (client) ID (for example, `41451fa7-82d9-4673-8fa5-69eff5a761fd`). + +In **Authentication** > **Platform configurations** > **Single-page application (SPA)**: + +1. Confirm the **Redirect URI** of `https://localhost/authentication/login-callback` is present. +1. In the **Implicit grant** section, ensure that the checkboxes for **Access tokens** and **ID tokens** are **not** selected. +1. The remaining defaults for the app are acceptable for this experience. +1. Select the **Save** button. + +Create the app. Replace the placeholders in the following command with the information recorded earlier and execute the following command in a command shell: + +```dotnetcli +dotnet new blazorwasm -au SingleOrg --client-id "{CLIENT ID}" --tenant-id "common" -o {APP NAME} +``` + +| Placeholder | Azure portal name | Example | +| ------------- | ----------------------- | -------------------------------------- | +| `{APP NAME}` | — | `BlazorSample` | +| `{CLIENT ID}` | Application (client) ID | `41451fa7-82d9-4673-8fa5-69eff5a761fd` | + +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. + +[!INCLUDE[](~/blazor/security/includes/additional-scopes-standalone-nonAAD.md)] + +After creating the app, you should be able to: + +* Log into the app using a Microsoft account. +* Request access tokens for Microsoft APIs. For more information, see: + * [Access token scopes](#access-token-scopes) + * [Quickstart: Configure an application to expose web APIs](/azure/active-directory/develop/quickstart-configure-app-expose-web-apis). + +## Authentication package + +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. + +[!INCLUDE[](~/includes/package-reference.md)] + +The [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package transitively adds the [`Microsoft.AspNetCore.Components.WebAssembly.Authentication`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication) package to the app. + +## Authentication service support + +Support for authenticating users is registered in the service container with the extension method provided by the [`Microsoft.Authentication.WebAssembly.Msal`](https://www.nuget.org/packages/Microsoft.Authentication.WebAssembly.Msal) package. This method sets up all of the services required for the app to interact with the Identity Provider (IP). + +`Program.cs`: + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); +}); +``` + +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 AAD configuration when you register the app. + +Configuration is supplied by the `wwwroot/appsettings.json` file: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/common", + "ClientId": "{CLIENT ID}", + "ValidateAuthority": true + } +} +``` + +Example: + +```json +{ + "AzureAd": { + "Authority": "https://login.microsoftonline.com/common", + "ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd", + "ValidateAuthority": true + } +} +``` + +## Access token scopes + +The Blazor WebAssembly template doesn't automatically configure the app to request an access token for a secure API. To provision an access token as part of the sign-in flow, add the scope to the default access token scopes of the : + +```csharp +builder.Services.AddMsalAuthentication(options => +{ + ... + options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}"); +}); +``` + +Specify additional scopes with `AdditionalScopesToConsent`: + +```csharp +options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}"); +``` + +For more information, see the following sections of the *Additional scenarios* article: + +* [Request additional access tokens](xref:blazor/security/webassembly/additional-scenarios#request-additional-access-tokens) +* [Attach tokens to outgoing requests](xref:blazor/security/webassembly/additional-scenarios#attach-tokens-to-outgoing-requests) + +## Login mode + +[!INCLUDE[](~/blazor/security/includes/msal-login-mode.md)] + +## Imports file + +[!INCLUDE[](~/blazor/security/includes/imports-file-standalone.md)] + +## Index page + +[!INCLUDE[](~/blazor/security/includes/index-page-msal.md)] + +## App component + +[!INCLUDE[](~/blazor/security/includes/app-component.md)] + +## RedirectToLogin component + +[!INCLUDE[](~/blazor/security/includes/redirecttologin-component.md)] + +## LoginDisplay component + +[!INCLUDE[](~/blazor/security/includes/logindisplay-component.md)] + +## Authentication component + +[!INCLUDE[](~/blazor/security/includes/authentication-component.md)] + [!INCLUDE[](~/blazor/security/includes/troubleshoot.md)] ## Additional resources