diff --git a/aspnetcore/blazor/components/index.md b/aspnetcore/blazor/components/index.md index 7007e486608d..bd693bb7798b 100644 --- a/aspnetcore/blazor/components/index.md +++ b/aspnetcore/blazor/components/index.md @@ -1854,6 +1854,10 @@ For more information, see the following resources: * * +## `IHttpContextAccessor`/`HttpContext` + +[!INCLUDE[](~/blazor/security/includes/httpcontext.md)] + [1]: [2]: diff --git a/aspnetcore/blazor/components/quickgrid.md b/aspnetcore/blazor/components/quickgrid.md index c68b5895f426..93219ef69aee 100644 --- a/aspnetcore/blazor/components/quickgrid.md +++ b/aspnetcore/blazor/components/quickgrid.md @@ -420,9 +420,18 @@ With the **Add New Scaffold Item** dialog open to **Installed** > **Common** > * Complete the **Add Razor Components using Entity Framework (CRUD)** dialog: + + * The **Template** dropdown list includes other templates for specifically creating create, edit, delete, details, and list components. This dropdown list comes in handy when you only need to create a specific type of component scaffolded to a model class. Leave the **Template** dropdown list set to **CRUD** to scaffold a full set of components. * In the **Model class** dropdown list, select the model class. A folder is created for the generated components from the model name (if the model class is named `Movie`, the folder is automatically named `MoviePages`). -* For **DbContext class**, select an existing database context or select the **+** (plus sign) button and **Add Data Context** modal dialog to add a new database context. +* For **DbContext class**, take either of the following approaches: + * Select an existing class that you know has a factory provider registration (). + * Select the **+** (plus sign) button and use the **Add Data Context** modal dialog to supply a new class name, which registers the class with a factory provider instead of using the context type directly as a service registration. * After the model dialog closes, the **Database provider** dropdown list defaults to **SQL Server**. You can select the appropriate provider for the database that you're using. The options include SQL Server, SQLite, PostgreSQL, and Azure Cosmos DB. * Select **Add**. @@ -468,7 +477,7 @@ The following table explains the ASP.NET Core code generator options in the prec Option | Placeholder | Description ------------- | -------------------- | --- `-dbProvider` | `{PROVIDER}` | Database provider to use. Options include `sqlserver` (default), `sqlite`, `cosmos`, `postgres`. -`-dc` | `{DB CONTEXT CLASS}` | The class to use, including the namespace. +`-dc` | `{DB CONTEXT CLASS}` | The class to use, including the namespace. To make sure that a class is registered in the app's services with a factory provider (), either use an existing class that you know has a factory provider registration or supply a new class. `-m` | `{MODEL}` | The name of the model class. `-outDir` | `{PATH}` | The output directory for the generated components. A folder is created from the model name in the output directory to hold the components (if the model class is named `Movie`, the folder is automatically named `MoviePages`). The path is typically either `Components/Pages` for a Blazor Web App or `Pages` for a standalone Blazor WebAssembly app. @@ -517,7 +526,7 @@ The following table explains the ASP.NET Core code generator options in the prec Option | Placeholder | Description ------------- | -------------------- | --- `-dbProvider` | `{PROVIDER}` | Database provider to use. Options include `sqlserver` (default), `sqlite`, `cosmos`, `postgres`. -`-dc` | `{DB CONTEXT CLASS}` | The class to use, including the namespace. +`-dc` | `{DB CONTEXT CLASS}` | The class to use, including the namespace. To make sure that a class is registered in the app's services with a factory provider (), either use an existing class that you know has a factory provider registration or supply a new class. `-m` | `{MODEL}` | The name of the model class. `-outDir` | `{PATH}` | The output directory for the generated components. A folder is created from the model name in the output directory to hold the components (if the model class is named `Movie`, the folder is automatically named `MoviePages`). The path is typically either `Components/Pages` for a Blazor Web App or `Pages` for a standalone Blazor WebAssembly app. diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index df23da6937ba..dfe5fc29341e 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -380,7 +380,7 @@ During static SSR, Razor component page requests are processed by server-side AS * [Not authorized content (`...`)](xref:blazor/security/index#authorizeview-component) (): Blazor Web Apps typically process unauthorized requests on the server by [customizing the behavior of Authorization Middleware](xref:security/authorization/authorizationmiddlewareresulthandler). -* [Not found content (`...`)](xref:blazor/fundamentals/routing#provide-custom-content-when-content-isnt-found) (): Blazor Web Apps typically process bad URL requests on the server by either displaying the browser's built-in 404 UI or returning a custom 404 page (or other response) via ASP.NET Core middleware (for example, [`UseStatusCodePagesWithRedirects`](xref:fundamentals/error-handling#usestatuscodepageswithredirects) / [API documentation](xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects%2A)). +* [Not found content (`...`)](xref:blazor/fundamentals/routing#provide-custom-content-when-content-isnt-found) (): Blazor Web Apps typically process bad URL requests on the server by either displaying the browser's built-in 404 UI or returning a custom 404 page (or other response) via ASP.NET Core middleware (for example, [`UseStatusCodePagesWithRedirects`](xref:fundamentals/error-handling#usestatuscodepageswithredirects) / [API documentation](xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithRedirects%2A)). For more information, see [Create simpler way to show Not Found 404 UI with Blazor (`dotnet/aspnetcore` #45654)](https://github.com/dotnet/aspnetcore/issues/45654). If the app exhibits root-level interactivity, server-side ASP.NET Core request processing isn't involved after the initial static SSR, which means that the preceding Blazor features work as expected. diff --git a/aspnetcore/blazor/fundamentals/environments.md b/aspnetcore/blazor/fundamentals/environments.md index e62c9bdf693c..7d0df2d16197 100644 --- a/aspnetcore/blazor/fundamentals/environments.md +++ b/aspnetcore/blazor/fundamentals/environments.md @@ -34,10 +34,10 @@ The environment is set using any of the following approaches: * Blazor Web App: Use any of the approaches described in for general ASP.NET Core apps. * Blazor Web App or standalone Blazor WebAssembly: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration) -* Standalone Blazor WebAssembly: [`blazor-environment` header](#set-the-client-side-environment-via-header) +* Standalone Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header) * Blazor Web App or standalone Blazor WebAssembly: [Azure App Service](#set-the-environment-for-azure-app-service) -On the client for a Blazor Web App, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `blazor-environment`. The header sets the environment when the is created in the client-side `Program` file (). +On the client for a Blazor Web App, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `Blazor-Environment`. The header sets the environment when the is created in the client-side `Program` file (). :::moniker-end @@ -47,14 +47,14 @@ The environment is set using any of the following approaches: * Blazor Server: Use any of the approaches described in for general ASP.NET Core apps. * Blazor Server or Blazor WebAssembly: [Blazor start configuration](#set-the-client-side-environment-via-blazor-startup-configuration) -* Blazor WebAssembly: [`blazor-environment` header](#set-the-client-side-environment-via-header) +* Blazor WebAssembly: [`Blazor-Environment` header](#set-the-client-side-environment-via-header) * Blazor Server or Blazor WebAssembly: [Azure App Service](#set-the-environment-for-azure-app-service) -On the client for a Blazor Web App or the client of a hosted Blazor WebAssembly app, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `blazor-environment`. The header sets the environment when the is created in the client-side `Program` file (). +On the client for a Blazor Web App or the client of a hosted Blazor WebAssembly app, the environment is determined from the server via a middleware that communicates the environment to the browser via a header named `Blazor-Environment`. The header sets the environment when the is created in the client-side `Program` file (). :::moniker-end -For a standalone Blazor WebAssembly app running locally, the development server adds the `blazor-environment` header. +For a standalone Blazor WebAssembly app running locally, the development server adds the `Blazor-Environment` header with the environment name obtained from the hosting environment. The hosting environment sets the environment from the `ASPNETCORE_ENVIRONMENT` environment variable established by the project's `Properties/launchSettings.json` file. The default value of the environment variable in a project created from the Blazor WebAssembly project template is `Development`. For more information, see the [Set the client-side environment via header](#set-the-client-side-environment-via-header) section. For app's running locally in development, the app defaults to the `Development` environment. Publishing the app defaults the environment to `Production`. @@ -111,9 +111,9 @@ Standalone Blazor WebAssembly: **In the preceding example, the `{BLAZOR SCRIPT}` placeholder is the Blazor script path and file name.** For the location of the script, see . -Using the `environment` property overrides the environment set by the [`blazor-environment` header](#set-the-client-side-environment-via-header). +Using the `environment` property overrides the environment set by the [`Blazor-Environment` header](#set-the-client-side-environment-via-header). -The preceding approach sets the client's environment without changing the `blazor-environment` header's value, nor does it change the server project's console logging of the startup environment for a Blazor Web App that has adopted global Interactive WebAssembly rendering. +The preceding approach sets the client's environment without changing the `Blazor-Environment` header's value, nor does it change the server project's console logging of the startup environment for a Blazor Web App that has adopted global Interactive WebAssembly rendering. To log the environment to the console in either a standalone Blazor WebAssembly project or the `.Client` project of a Blazor Web App, place the following C# code in the `Program` file after the is created with and before the line that builds and runs the project (`await builder.Build().RunAsync();`): @@ -126,20 +126,24 @@ For more information on Blazor startup, see . ## Set the client-side environment via header -Blazor WebAssembly apps can set the environment with the `blazor-environment` header. +Blazor WebAssembly apps can set the environment with the `Blazor-Environment` header. Specifically, the response header must be set on the `_framework/blazor.boot.json` file, but there's no harm setting the header on file server responses for other Blazor file requests or the entire Blazor deployment. -In the following example for IIS, the custom header (`blazor-environment`) is added to the published `web.config` file. The `web.config` file is located in the `bin/Release/{TARGET FRAMEWORK}/publish` folder, where the `{TARGET FRAMEWORK}` placeholder is the target framework: +Although the Blazor framework issues the header name in kebab case with mixed letter case (`Blazor-Environment`), you're welcome to use all-lower or all-upper kebab case (`blazor-environment`, `BLAZOR-ENVIRONMENT`). + +For local development runs with Blazor's built-in development server, you can control the value of the `Blazor-Environment` header by setting the value of the `ASPNETCORE_ENVIRONMENT` environment variable in the project's `Properties/launchSettings.json` file. When running locally with the development server, the order of precedence for determining the app's environment is [`Blazor.start` configuration (`environment` key)](#set-the-client-side-environment-via-blazor-startup-configuration) > `Blazor-Environment` response header (`blazor.boot.json` file) > `ASPNETCORE_ENVIRONMENT` environment variable (`launchSettings.json`). You can't use the `ASPNETCORE_ENVIRONMENT` environment variable (`launchSettings.json`) approach for a deployed Blazor WebAssembly app. The technique only works with the development server on local runs of the app. + +### IIS + +In the following example for IIS, the custom header (`Blazor-Environment`) is added to the published `web.config` file. The `web.config` file is located in the `bin/Release/{TARGET FRAMEWORK}/publish` folder, where the `{TARGET FRAMEWORK}` placeholder is the target framework: ```xml - ... - - + @@ -148,8 +152,44 @@ In the following example for IIS, the custom header (`blazor-environment`) is ad > [!NOTE] > To use a custom `web.config` file for IIS that isn't overwritten when the app is published to the `publish` folder, see . -> -> Although the Blazor framework issues the header name in all lowercase letters (`blazor-environment`), you're welcome to use any casing that you desire. For example, a header name that capitalizes each word (`Blazor-Environment`) is supported. + +### Nginx + +For Nginx servers, use the `add_header` directive from the `ngx_http_headers_module`: + +``` +http { + server { + ... + location / { + ... + add_header Blazor-Environment "Staging"; + } + } +} +``` + +For more information, see the following resources: + +* [Nginx documentation](http://nginx.org/docs/http/ngx_http_headers_module.html) +* [Blazor WebAssembly Nginx hosting guidance](xref:blazor/host-and-deploy/webassembly#nginx) + +### Apache + +For Apache servers, use the `Header` directive from the `mod_headers` module: + +``` + + ... + Header set Blazor-Environment "Staging" + ... + +``` + +For more information: + +* [Apache documentation (search the latest release for "`mod_headers`")](https://httpd.apache.org/docs/) +* [Blazor WebAssembly Apache hosting guidance](xref:blazor/host-and-deploy/webassembly#apache) ## Set the environment for Azure App Service @@ -157,7 +197,7 @@ In the following example for IIS, the custom header (`blazor-environment`) is ad case sensitivity is tracked for 10.0 by ... https://github.com/dotnet/aspnetcore/issues/25152 --> -For a standalone Blazor WebAssembly app, you can set the environment manually via [start configuration](#set-the-client-side-environment-via-blazor-startup-configuration) or the [`blazor-environment` header](#set-the-client-side-environment-via-header). +For a standalone Blazor WebAssembly app, you can set the environment manually via [start configuration](#set-the-client-side-environment-via-blazor-startup-configuration) or the [`Blazor-Environment` header](#set-the-client-side-environment-via-header). For a server-side app, set the environment via an `ASPNETCORE_ENVIRONMENT` app setting in Azure: @@ -169,7 +209,7 @@ For a server-side app, set the environment via an `ASPNETCORE_ENVIRONMENT` app s When requested in a browser, the `BlazorAzureAppSample/Staging` app loads in the `Staging` environment at `https://blazorazureappsample-staging.azurewebsites.net`. -When the app is loaded in the browser, the response header collection for `blazor.boot.json` indicates that the `blazor-environment` header value is `Staging`. +When the app is loaded in the browser, the response header collection for `blazor.boot.json` indicates that the `Blazor-Environment` header value is `Staging`. App settings from the `appsettings.{ENVIRONMENT}.json` file are loaded by the app, where the `{ENVIRONMENT}` placeholder is the app's environment. In the preceding example, settings from the `appsettings.Staging.json` file are loaded. diff --git a/aspnetcore/blazor/fundamentals/index.md b/aspnetcore/blazor/fundamentals/index.md index 7fe074617b1f..18882eae41db 100644 --- a/aspnetcore/blazor/fundamentals/index.md +++ b/aspnetcore/blazor/fundamentals/index.md @@ -179,12 +179,32 @@ Documentation sample apps are available for inspection and download: Locate a sample app by first selecting the version folder that matches the version of .NET that you're working with. -:::moniker range=">= aspnetcore-8.0" +:::moniker range=">= aspnetcore-9.0" + +Samples apps in the repository: + +* Blazor Web App +* Blazor WebAssembly +* Blazor Web App Movies tutorial sample () +* Blazor Web App with EF Core () +* Blazor Web App with SignalR () +* Two Blazor Web Apps and a Blazor WebAssembly app for calling web (server) APIs () +* Blazor Web App with OIDC (BFF and non-BFF patterns) () +* Blazor Web App with Entra () +* Blazor WebAssembly scopes-enabled logging () +* Blazor WebAssembly with ASP.NET Core Identity () +* .NET MAUI Blazor Hybrid app with a Blazor Web App and a shared UI provided by a Razor class library (RCL) () +* Additional samples (see the [Blazor samples GitHub repository README file](https://github.com/dotnet/blazor-samples)) + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" Samples apps in the repository: * Blazor Web App * Blazor WebAssembly +* Blazor Web App Movies tutorial sample () * Blazor Web App with EF Core () * Blazor Web App with SignalR () * Two Blazor Web Apps and a Blazor WebAssembly app for calling web (server) APIs () @@ -192,6 +212,7 @@ Samples apps in the repository: * Blazor WebAssembly scopes-enabled logging () * Blazor WebAssembly with ASP.NET Core Identity () * .NET MAUI Blazor Hybrid app with a Blazor Web App and a shared UI provided by a Razor class library (RCL) () +* Additional samples (see the [Blazor samples GitHub repository README file](https://github.com/dotnet/blazor-samples)) :::moniker-end @@ -207,7 +228,7 @@ The sample repo contains two types of samples: :::moniker-end -For more information and a list of the samples in the repository, see the [Blazor samples GitHub repository README.md file](https://github.com/dotnet/blazor-samples). +For more information, a list of the samples in the repository, and download instructions, see the [Blazor samples GitHub repository README file](https://github.com/dotnet/blazor-samples). The ASP.NET Core repository's Basic Test App is also a helpful set of samples for various Blazor scenarios: @@ -215,11 +236,6 @@ The ASP.NET Core repository's Basic Test App is also a helpful set of samples fo [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] -To download the sample apps: - -* Download the [Blazor samples repository](https://github.com/dotnet/blazor-samples) ZIP file. -* Unzip the file. - ## Byte multiples .NET byte sizes use metric prefixes for non-decimal multiples of bytes based on powers of 1024. diff --git a/aspnetcore/blazor/fundamentals/routing.md b/aspnetcore/blazor/fundamentals/routing.md index 26d06edf228a..d9ad24d2b20b 100644 --- a/aspnetcore/blazor/fundamentals/routing.md +++ b/aspnetcore/blazor/fundamentals/routing.md @@ -156,7 +156,11 @@ Arbitrary items are supported as content of the [!IMPORTANT] -> Blazor Web Apps don't use the parameter (`...` markup), but the parameter is supported for backward compatibility to avoid a breaking change in the framework. The server-side ASP.NET Core middleware pipeline processes requests on the server. Use server-side techniques to handle bad requests. For more information, see . +> Blazor Web Apps don't use the parameter (`...` markup), but the parameter is supported† for backward compatibility to avoid a breaking change in the framework. The server-side ASP.NET Core middleware pipeline processes requests on the server. Use server-side techniques to handle bad requests. +> +> †*Supported* in this context means that placing `...` markup doesn't result in an exception, but using the markup isn't effective either. +> +> For more information, including a recommended approach for handling bad requests, see . :::moniker-end diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index 4644e9951b9e..04d41fd265a0 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -1452,7 +1452,62 @@ Use a to c :::moniker-end -## `IHttpContextAccessor`/`HttpContext` in Razor components +## Start the SignalR circuit at a different URL + +Prevent automatically starting the app by adding `autostart="false"` to the Blazor ` ++ ++ +``` + +Blazor Server: + +:::moniker-end + +```diff +- ++ ++ +``` + +Add the following call with the hub path to the middleware processing pipeline in the server app's `Program` file. + +:::moniker range=">= aspnetcore-8.0" + +Blazor Web Apps: + +```csharp +app.MapBlazorHub("/signalr"); +``` + +Blazor Server: + +:::moniker-end + +Leave the existing call to in the file and add a new call to with the path: + +```diff +app.MapBlazorHub(); ++ app.MapBlazorHub("/signalr"); +``` + +## `IHttpContextAccessor`/`HttpContext` [!INCLUDE[](~/blazor/security/includes/httpcontext.md)] diff --git a/aspnetcore/blazor/includes/js-interop/js-collocation.md b/aspnetcore/blazor/includes/js-interop/js-collocation.md index 647b8b29cc95..4170582bf5cc 100644 --- a/aspnetcore/blazor/includes/js-interop/js-collocation.md +++ b/aspnetcore/blazor/includes/js-interop/js-collocation.md @@ -50,7 +50,7 @@ Add the following script after the Blazor script ([location of the Blazor start @code { private string? result; - public async void ShowPrompt() + public async Task ShowPrompt() { result = await JS.InvokeAsync( "showPrompt1", "What's your name?"); diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index f974c5cae4e7..b11bc8266913 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -5,7 +5,7 @@ description: Learn how to invoke .NET methods from JavaScript functions in Blazo monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 11/12/2024 +ms.date: 12/17/2024 uid: blazor/js-interop/call-dotnet-from-javascript --- # Call .NET methods from JavaScript functions in ASP.NET Core Blazor @@ -757,7 +757,7 @@ public class GenericType } [JSInvokable] - public async void UpdateAsync(TValue newValue) + public async Task UpdateAsync(TValue newValue) { await Task.Yield(); Value = newValue; diff --git a/aspnetcore/blazor/javascript-interoperability/index.md b/aspnetcore/blazor/javascript-interoperability/index.md index d4b7adc104d1..284a7141e461 100644 --- a/aspnetcore/blazor/javascript-interoperability/index.md +++ b/aspnetcore/blazor/javascript-interoperability/index.md @@ -315,6 +315,13 @@ export class DOMCleanup { window.DOMCleanup = DOMCleanup; ``` +The preceding approaches attach the `MutationObserver` to `target.parentNode`, which works until `parentNode` itself is removed from the DOM. This is a common scenario, for example, when navigating to a new page, which causes the entire page component to be removed from the DOM. In such cases, any child components observing changes within the page aren't cleaned up properly. + +Don't assume that observing `document.body`, instead of `target.parentNode`, is a better target. Observing `document.body` has performance implications because callback logic is executed for *all* DOM updates, whether or not they have anything to do with your element. Use either of the following approaches: + +* In cases where you can identify a suitable ancestor node to observe, use `MutationObserver` with it. Ideally, this ancestor is scoped to the changes that you want to observe, rather than `document.body`. +* Instead of using `MutationObserver`, consider using a [custom element and `disconnectedCallback`](https://developer.mozilla.org/docs/Web/API/Web_components/Using_custom_elements). The event always fires when your custom element is disconnected, no matter where it resides in the DOM relative to the DOM change. + ## JavaScript interop calls without a circuit *This section only applies to server-side apps.* diff --git a/aspnetcore/blazor/security/blazor-web-app-with-entra.md b/aspnetcore/blazor/security/blazor-web-app-with-entra.md index 4a81ea3ba0af..5c4fbfedfd54 100644 --- a/aspnetcore/blazor/security/blazor-web-app-with-entra.md +++ b/aspnetcore/blazor/security/blazor-web-app-with-entra.md @@ -95,6 +95,24 @@ Example: The callback path (`CallbackPath`) must match the redirect URI (login callback path) configured when registering the application in the Entra or Azure portal. Paths are configured in the **Authentication** blade of the app's registration. The default value of `CallbackPath` is `/signin-oidc` for a registered redirect URI of `https://localhost/signin-oidc` (a port isn't required). +The (configuration key: "`SignedOutCallbackPath`") is the request path within the app's base path intercepted by the OpenID Connect handler where the user agent is first returned after signing out from Entra. The sample app doesn't set a value for the path because the default value of "`/signout-callback-oidc`" is used. After intercepting the request, the OpenID Connect handler redirects to the or , if specified. + +Configure the signed-out callback path in the app's Entra registration. In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI** entries: + +> :::no-loc text="https://localhost/signout-callback-oidc"::: + +> [!NOTE] +> A port isn't required for `localhost` addresses when using Entra. + +If you don't add the signed-out callback path URI to the app's registration in Entra, Entra refuses to redirect the user back to the app and merely asks them to close their browser window. + + + +> [!NOTE] +> Entra doesn't redirect a primary admin user (root account) or external user back to the Blazor application. Instead, Entra logs the user out of the app and recommends that they close all of their browser windows. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783#issuecomment-1465217522). + [!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)] ### Establish the client secret diff --git a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md index cd03d687c79a..df4d97fc1528 100644 --- a/aspnetcore/blazor/security/blazor-web-app-with-oidc.md +++ b/aspnetcore/blazor/security/blazor-web-app-with-oidc.md @@ -27,6 +27,8 @@ The following specification is covered: * Automatic non-interactive token refresh. * Securely calls a (web) API in the server project for data. +For an alternative experience using [Microsoft Authentication Library for .NET](/entra/msal/dotnet/), [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/), and [Microsoft Entra ID](https://www.microsoft.com/security/business/identity-access/microsoft-entra-id), see . + ## Sample app The sample app consists of two projects: @@ -44,9 +46,6 @@ The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . -> [!NOTE] -> The server project uses /, but never for interactively-rendered components. For more information, see . - ### Configuration This section explains how to configure the sample app. @@ -130,14 +129,15 @@ The following : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. - - In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, do **not** select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. +* : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. ```csharp oidcOptions.ResponseType = OpenIdConnectResponseType.Code; ``` + > [!NOTE] + > In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, do **not** select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**. + * and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When is set to `false`, the handler doesn't perform claims mappings, and the claim names from the JWT are used directly by the app. The following example sets the role claim type to "`roles`," which is appropriate for [Microsoft Entra ID (ME-ID)](https://www.microsoft.com/security/business/microsoft-entra). Consult your identity provider's documentation for more information. > [!NOTE] @@ -153,33 +153,30 @@ The following : The request path within the app's base path where the user-agent is returned. - In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port: - > :::no-loc text="https://localhost/signin-oidc"::: + > :::no-loc text="https://localhost:{PORT}/signin-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. + > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require the correct port. - * : The request path within the app's base path where the user agent is returned after sign out from the identity provider. + * (configuration key: "`SignedOutCallbackPath`"): The request path within the app's base path intercepted by the OIDC handler where the user agent is first returned after signing out from the identity provider. The sample app doesn't set a value for the path because the default value of "`/signout-callback-oidc`" is used. After intercepting the request, the OIDC handler redirects to the or , if specified. - In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port: - > :::no-loc text="https://localhost/signout-callback-oidc"::: + > :::no-loc text="https://localhost:{PORT}/signout-callback-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. - - > [!NOTE] - > If using Microsoft Identity Web, the provider currently only redirects back to the if the `microsoftonline.com` Authority (`https://login.microsoftonline.com/{TENANT ID}/v2.0/`) is used. This limitation doesn't exist if you can use the "common" Authority with Microsoft Identity Web. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783). + > When using Microsoft Entra ID, set the path in the **Web** platform configuration's **Redirect URI** entries in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. If you don't add the signed-out callback path URI to the app's registration in Entra, Entra refuses to redirect the user back to the app and merely asks them to close their browser window. * : Requests received on this path cause the handler to invoke sign-out using the sign-out scheme. - In the Entra or Azure portal, set the **Front-channel logout URL**: + In the following example, the `{PORT}` placeholder is the app's port: > :::no-loc text="https://localhost/signout-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require a correct port. + > When using Microsoft Entra ID, set the **Front-channel logout URL** in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. ```csharp oidcOptions.CallbackPath = new PathString("{PATH}"); @@ -306,9 +303,6 @@ The `BlazorWebAppOidc` project is the server-side project of the Blazor Web App. The `BlazorWebAppOidc.http` file can be used for testing the weather data request. Note that the `BlazorWebAppOidc` project must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see . -> [!NOTE] -> The server project uses /, but never for interactively-rendered components. For more information, see . - ### Configuration This section explains how to configure the sample app. @@ -367,12 +361,15 @@ The following ): The `Weather.Get` scope is configured in the Azure or Entra portal under **Expose an API**. This is necessary for backend web API project (`MinimalApiJwt`) to validate the access token with bearer JWT. +* Scopes for obtaining weather data from the web API (): This is necessary for backend web API project (`MinimalApiJwt`) to validate the access token with bearer JWT. ```csharp oidcOptions.Scope.Add("{APP ID URI}/{API NAME}"); ``` + > [!NOTE] + > When using Microsoft Entra ID, the `Weather.Get` scope is configured in the Azure or Entra portal under **Expose an API**. + Example: * App ID URI (`{APP ID URI}`): `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}` @@ -420,14 +417,15 @@ The following : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. - - In the Entra or Azure portal's **Implicit grant and hybrid flows** app registration configuration, do **not** select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens**. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. +* : Configures the OIDC handler to only perform authorization code flow. Implicit grants and hybrid flows are unnecessary in this mode. The OIDC handler automatically requests the appropriate tokens using the code returned from the authorization endpoint. ```csharp oidcOptions.ResponseType = OpenIdConnectResponseType.Code; ``` + > [!NOTE] + > When using Microsoft Entra ID, do **not** select either checkbox for the authorization endpoint to return **Access tokens** or **ID tokens** in **Implicit grant and hybrid flows** app registration configuration. + * and configuration of and : Many OIDC servers use "`name`" and "`role`" rather than the SOAP/WS-Fed defaults in . When is set to `false`, the handler doesn't perform claims mappings and the claim names from the JWT are used directly by the app. The following example sets the role claim type to "`roles`," which is appropriate for [Microsoft Entra ID (ME-ID)](https://www.microsoft.com/security/business/microsoft-entra). Consult your identity provider's documentation for more information. > [!NOTE] @@ -441,35 +439,30 @@ The following : The request path within the app's base path where the user-agent is returned. - - In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port: - > :::no-loc text="https://localhost/signin-oidc"::: + > :::no-loc text="https://localhost:{PORT}/signin-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. + > A port isn't required for `localhost` addresses when using Microsoft Entra ID. Most other OIDC providers require the correct port. - * : The request path within the app's base path where the user agent is returned after sign out from the identity provider. + * (configuration key: "`SignedOutCallbackPath`"): The request path within the app's base path intercepted by the OIDC handler where the user agent is first returned after signing out from the identity provider. The sample app doesn't set a value for the path because the default value of "`/signout-callback-oidc`" is used. After intercepting the request, the OIDC handler redirects to the or , if specified. - In the Entra or Azure portal, set the path in the **Web** platform configuration's **Redirect URI**: + Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port: - > :::no-loc text="https://localhost/signout-callback-oidc"::: + > :::no-loc text="https://localhost:{PORT}/signout-callback-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. - - > [!NOTE] - > If using Microsoft Identity Web, the provider currently only redirects back to the if the `microsoftonline.com` Authority (`https://login.microsoftonline.com/{TENANT ID}/v2.0/`) is used. This limitation doesn't exist if you can use the "common" Authority with Microsoft Identity Web. For more information, see [postLogoutRedirectUri not working when authority url contains a tenant ID (`AzureAD/microsoft-authentication-library-for-js` #5783)](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5783). + > When using Microsoft Entra ID, set the path in the **Web** platform configuration's **Redirect URI** entries in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. If you don't add the signed-out callback path URI to the app's registration in Entra, Entra refuses to redirect the user back to the app and merely asks them to close their browser window. * : Requests received on this path cause the handler to invoke sign-out using the sign-out scheme. - In the Entra or Azure portal, set the **Front-channel logout URL**: + In the following example, the `{PORT}` placeholder is the app's port: > :::no-loc text="https://localhost/signout-oidc"::: > [!NOTE] - > A port isn't required for `localhost` addresses. + > When using Microsoft Entra ID, set the **Front-channel logout URL** in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. ```csharp oidcOptions.CallbackPath = new PathString("{PATH}"); @@ -503,7 +496,7 @@ Inspect the sample app for the following features: * Automatic non-interactive token refresh with the help of a custom cookie refresher (`CookieOidcRefresher.cs`). * The server project calls to add a server-side authentication state provider that uses to flow the authentication state to the client. The client calls to deserialize and use the authentication state passed by the server. The authentication state is fixed for the lifetime of the WebAssembly application. * Requests to the Blazor Web App are proxied to the backend web API project (`MinimalApiJwt`). `MapForwarder` in the `Program` file adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using default configuration for the outgoing request, customized transforms, and default HTTP client: - * When rendering the `Weather` component on the server, the component uses the `ServerWeatherForecaster` to proxy the request for weather data with the user's access token. + * When rendering the `Weather` component on the server, the component uses the `ServerWeatherForecaster` class to proxy the request for weather data with the user's access token. determines if an is available for use by the `GetWeatherForecastAsync` method. For more information, see . * When the component is rendered on the client, the component uses the `ClientWeatherForecaster` service implementation, which uses a preconfigured (in the client project's `Program` file) to make a web API call to the server project. A Minimal API endpoint (`/weather-forecast`) defined in the server project's `Program` file transforms the request with the user's access token to obtain the weather data. :::moniker-end @@ -513,7 +506,7 @@ Inspect the sample app for the following features: * Automatic non-interactive token refresh with the help of a custom cookie refresher (`CookieOidcRefresher.cs`). * The `PersistingAuthenticationStateProvider` class (`PersistingAuthenticationStateProvider.cs`) is a server-side that uses to flow the authentication state to the client, which is then fixed for the lifetime of the WebAssembly application. * Requests to the Blazor Web App are proxied to the backend web API project (`MinimalApiJwt`). `MapForwarder` in the `Program` file adds direct forwarding of HTTP requests that match the specified pattern to a specific destination using default configuration for the outgoing request, customized transforms, and default HTTP client: - * When rendering the `Weather` component on the server, the component uses the `ServerWeatherForecaster` to proxy the request for weather data with the user's access token. + * When rendering the `Weather` component on the server, the component uses the `ServerWeatherForecaster` class to proxy the request for weather data with the user's access token. determines if an is available for use by the `GetWeatherForecastAsync` method. For more information, see . * When the component is rendered on the client, the component uses the `ClientWeatherForecaster` service implementation, which uses a preconfigured (in the client project's `Program` file) to make a web API call to the server project. A Minimal API endpoint (`/weather-forecast`) defined in the server project's `Program` file transforms the request with the user's access token to obtain the weather data. :::moniker-end @@ -548,14 +541,15 @@ The `MinimalApiJwt` project is a backend web API for multiple frontend projects. Configure the project in the of the call in the project's `Program` file: -* : Sets the Audience for any received OpenID Connect token. - - In the Azure or Entra portal: Match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API**: +* : Sets the Audience for any received OIDC token. ```csharp jwtOptions.Audience = "{APP ID URI}"; ``` + > [!NOTE] + > When using Microsoft Entra ID, match the value to just the path of the **Application ID URI** configured when adding the `Weather.Get` scope under **Expose an API** in the Azure or Entra portal. + Example: App ID URI (`{APP ID URI}`): `https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}`: @@ -577,7 +571,7 @@ Configure the project in the : Sets the Authority for making OpenID Connect calls. Match the value to the Authority configured for the OIDC handler in `BlazorWebAppOidc/Program.cs`: +* : Sets the Authority for making OIDC calls. Match the value to the Authority configured for the OIDC handler in `BlazorWebAppOidc/Program.cs`: ```csharp jwtOptions.Authority = "{AUTHORITY}"; diff --git a/aspnetcore/blazor/security/includes/httpcontext.md b/aspnetcore/blazor/security/includes/httpcontext.md index 446714d8e5a6..815c80e16a52 100644 --- a/aspnetcore/blazor/security/includes/httpcontext.md +++ b/aspnetcore/blazor/security/includes/httpcontext.md @@ -1,6 +1,6 @@ :::moniker range=">= aspnetcore-8.0" - must be avoided with interactive rendering because there isn't a valid `HttpContext` available. + generally should be avoided with interactive rendering because a valid isn't always available. can be used for components that are statically rendered on the server. **However, we recommend avoiding it if possible.** diff --git a/aspnetcore/blazor/security/index.md b/aspnetcore/blazor/security/index.md index 6162f9d67a77..b888a257551e 100644 --- a/aspnetcore/blazor/security/index.md +++ b/aspnetcore/blazor/security/index.md @@ -108,7 +108,7 @@ The built-in or custom . -### `IHttpContextAccessor`/`HttpContext` in Razor components +### `IHttpContextAccessor`/`HttpContext` [!INCLUDE[](~/blazor/security/includes/httpcontext.md)] diff --git a/aspnetcore/blazor/security/interactive-server-side-rendering.md b/aspnetcore/blazor/security/interactive-server-side-rendering.md index 22617f8290ef..f7ff8bff26bd 100644 --- a/aspnetcore/blazor/security/interactive-server-side-rendering.md +++ b/aspnetcore/blazor/security/interactive-server-side-rendering.md @@ -60,7 +60,7 @@ In general, we recommend that you avoid rendering components that contain sensit [!INCLUDE[](~/blazor/security/includes/shared-state.md)] -## `IHttpContextAccessor`/`HttpContext` in Razor components +## `IHttpContextAccessor`/`HttpContext` [!INCLUDE[](~/blazor/security/includes/httpcontext.md)] diff --git a/aspnetcore/fundamentals/middleware/index.md b/aspnetcore/fundamentals/middleware/index.md index c00d173d5140..7cb4afef2090 100644 --- a/aspnetcore/fundamentals/middleware/index.md +++ b/aspnetcore/fundamentals/middleware/index.md @@ -27,6 +27,12 @@ Request delegates are configured using explains the difference between request pipelines in ASP.NET Core and ASP.NET 4.x and provides additional middleware samples. +## The role of middleware by app type + +Blazor Web Apps, Razor Pages, and MVC process browser requests on the server with middleware. The guidance in this article applies to these types of apps. + +Standalone Blazor WebAssembly apps run entirely on the client and don't process requests with a middleware pipeline. The guidance in this article doesn't apply to standalone Blazor WebAssembly apps. + ## Middleware code analysis ASP.NET Core includes many compiler platform analyzers that inspect application code for quality. For more information, see diff --git a/aspnetcore/fundamentals/middleware/index/includes/index3-7.md b/aspnetcore/fundamentals/middleware/index/includes/index3-7.md index 19f57df6aff4..ca93acdb78fe 100644 --- a/aspnetcore/fundamentals/middleware/index/includes/index3-7.md +++ b/aspnetcore/fundamentals/middleware/index/includes/index3-7.md @@ -13,6 +13,12 @@ Request delegates are configured using explains the difference between request pipelines in ASP.NET Core and ASP.NET 4.x and provides additional middleware samples. +## The role of middleware by app type + +Razor Pages, MVC, Blazor Server, and the server-side project of a hosted Blazor WebAssembly solution process browser requests on the server with middleware. The guidance in this article applies to these types of apps. + +Standalone Blazor WebAssembly apps run entirely on the client and don't process requests with a middleware pipeline. The guidance in this article doesn't apply to standalone Blazor WebAssembly apps. + ## Middleware code analysis ASP.NET Core includes many compiler platform analyzers that inspect application code for quality. For more information, see diff --git a/aspnetcore/fundamentals/openapi/include-metadata.md b/aspnetcore/fundamentals/openapi/include-metadata.md index 0256162def1b..185d7f4f3911 100644 --- a/aspnetcore/fundamentals/openapi/include-metadata.md +++ b/aspnetcore/fundamentals/openapi/include-metadata.md @@ -26,7 +26,7 @@ The following table provides an overview of the metadata collected and the strat | tags | [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) | | | | operationId | [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) | | | | parameters | [`[FromQuery]`](xref:Microsoft.AspNetCore.Mvc.FromQueryAttribute), [`[FromRoute]`](xref:Microsoft.AspNetCore.Mvc.FromRouteAttribute), [`[FromHeader]`](xref:Microsoft.AspNetCore.Mvc.FromHeaderAttribute), [`[FromForm]`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) | | -| parameter description | [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) | | | +| parameter description | [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) | | | | requestBody | [`[FromBody]`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) | | | | responses | [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) | , | | | Excluding endpoints | [`[ExcludeFromDescription]`](xref:Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute), [`[ApiExplorerSettings]`](xref:Microsoft.AspNetCore.Mvc.ApiExplorerSettingsAttribute) | | | @@ -148,7 +148,7 @@ OpenAPI supports annotating path, query string, header, and cookie parameters th The framework infers the types for request parameters automatically based on the signature of the route handler. -The [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) attribute can be used to provide a description for a parameter. +The [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) attribute can be used to provide a description for a parameter. #### [Minimal APIs](#tab/minimal-apis) diff --git a/aspnetcore/fundamentals/use-http-context.md b/aspnetcore/fundamentals/use-http-context.md index 05266eb35304..6ca6c5c57305 100644 --- a/aspnetcore/fundamentals/use-http-context.md +++ b/aspnetcore/fundamentals/use-http-context.md @@ -4,7 +4,7 @@ author: jamesnk description: How to use HttpContext in ASP.NET Core. monikerRange: '>= aspnetcore-3.1' ms.author: wpickett -ms.date: 10/07/2024 +ms.date: 12/13/2024 uid: fundamentals/use-httpcontext --- @@ -13,9 +13,7 @@ uid: fundamentals/use-httpcontext [!INCLUDE[](~/includes/not-latest-version.md)] - encapsulates all information about an individual HTTP request and response. An `HttpContext` instance is initialized when an HTTP request is received. The `HttpContext` instance is accessible by middleware and app frameworks such as Web API controllers, Razor Pages, SignalR, gRPC, and more. - -For more information about accessing the `HttpContext`, see . + encapsulates all information about an individual HTTP request and response. An `HttpContext` instance is initialized when an HTTP request is received. The `HttpContext` instance is accessible by middleware and app frameworks such as Blazor Web Apps, Web API controllers, Razor Pages, SignalR, gRPC, and more. ## `HttpRequest` @@ -97,7 +95,7 @@ Commonly used members on `HttpResponse` include: provides access to the response headers sent with the HTTP response. There are two ways to access headers using this collection: * Provide the header name to the indexer on the header collection. The header name isn't case-sensitive. The indexer can access any header value. -* The header collection also has properties for getting and setting commonly used HTTP headers. The properties provide a fast, IntelliSense driven way to access headers. +* Use the header collection properties for getting and setting commonly used HTTP headers. The properties provide a fast, IntelliSense driven way to access headers. [!code-csharp[](use-http-context/samples/Program.cs?name=snippet_ResponseHeaders&highlight=6-7)] @@ -182,13 +180,13 @@ For more information about using request features and `HttpContext`, see interface should be used with caution. As always, the `HttpContext` must ***not*** be captured outside of the request flow. `IHttpContextAccessor`: - * Relies on which can have a negative performance impact on asynchronous calls. +* The `HttpContext` is **NOT** thread safe. Accessing it from multiple threads can result in unpredictable results, such as exceptions and data corruption. +* The interface should be used with caution. As always, the `HttpContext` must ***not*** be captured outside of the request flow. `IHttpContextAccessor`: + * Relies on , which can have a negative performance impact on asynchronous calls. * Creates a dependency on "ambient state" which can make testing more difficult. -* may be `null` if accessed outside of the request flow. +* might be `null` if accessed outside of the request flow. * To access information from `HttpContext` outside the request flow, copy the information inside the request flow. Be careful to copy the actual data and not just references. For example, rather than copying a reference to an `IHeaderDictionary`, copy the relevant header values or copy the entire dictionary key by key before leaving the request flow. * Don't capture `IHttpContextAccessor.HttpContext` in a constructor. @@ -219,3 +217,7 @@ The application also includes `PeriodicBranchesLoggerService`, which logs the op `PeriodicBranchesLoggerService` is a [hosted service](xref:fundamentals/host/hosted-services), which runs outside the request and response flow. Logging from the `PeriodicBranchesLoggerService` has a null `HttpContext`. The `PeriodicBranchesLoggerService` was written to not depend on the `HttpContext`. [!code-csharp[](~/fundamentals/http-context/samples/6.x/HttpContextInBackgroundThread/Program.cs?highlight=8&range=1-11)] + +## Additional resources + +For more information about accessing `HttpContext`, see . diff --git a/aspnetcore/security/authentication/configure-oidc-web-authentication.md b/aspnetcore/security/authentication/configure-oidc-web-authentication.md index 52c66755d823..75caf91d62c8 100644 --- a/aspnetcore/security/authentication/configure-oidc-web-authentication.md +++ b/aspnetcore/security/authentication/configure-oidc-web-authentication.md @@ -23,6 +23,10 @@ This article covers the following areas: * Backend for frontend (BFF) security architecture * Advanced features, standards, extending the an OpenID Connect client +For an alternative experience using [Microsoft Authentication Library for .NET](/entra/msal/dotnet/), [Microsoft Identity Web](/entra/msal/dotnet/microsoft-identity-web/), and [Microsoft Entra ID](https://www.microsoft.com/security/business/identity-access/microsoft-entra-id), see [Quickstart: Sign in users and call the Microsoft Graph API from an ASP.NET Core web app (Azure documentation)](/entra/identity-platform/quickstart-web-app-dotnet-core-sign-in). + +For an example using Microsoft Entra External ID OIDC server, see [Sign in users for a sample ASP.NET Core web app in an external tenant](/entra/external-id/customers/sample-web-app-dotnet-sign-in) and [An ASP.NET Core web app authenticating users against Microsoft Entra External ID using Microsoft Identity Web](/samples/azure-samples/ms-identity-ciam-dotnet-tutorial/ms-identity-ciam-dotnet-tutorial-1-sign-in-aspnet-core-mvc/). + ## What is an OpenID Connect confidential interactive client [OpenID Connect](https://openid.net/developers/how-connect-works/) can be used to implement authentication in ASP.NET Core applications. The recommended way is to use an OpenID Connect confidential client using the code flow. Using the [Proof Key for Code Exchange by OAuth Public Clients (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) is recommended for this implementation. Both the application client and the user of the application are authenticated in the confidential flow. The application client uses a client secret or a client assertion to authenticate. @@ -44,15 +48,15 @@ The following section shows how to implement an OpenID Connect client in an empt ### Add OpenID Connect support -Add the [Microsoft.AspNetCore.Authentication.OpenIdConnect](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect) Nuget packages to the ASP.NET Core project. +Add the [`Microsoft.AspNetCore.Authentication.OpenIdConnect`](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.OpenIdConnect) Nuget packages to the ASP.NET Core project. ### Setup the OpenID Connect client -Add the authentication to the web application using the builder.Services in the **Program.cs** file. The configuration is dependent on the OpenID Connect server. Each OpenID Connect server requires small differences in the setup. +Add the authentication to the web application using the builder.Services in the `Program.cs` file. The configuration is dependent on the OpenID Connect server. Each OpenID Connect server requires small differences in the setup. The OpenID Connect handler is used for challenges and signout. The cookie is used to handle the session in the web application. The default schemes for the authentication can be specified as required. -See the ASP.NET Core [authentication-handler](xref: security/authentication/index?view=aspnetcore-8.0#authentication-handler) for details. +For more information, see the [ASP.NET Core `authentication-handler` guidance](xref:security/authentication/index#authentication-handler). ```csharp builder.Services.AddAuthentication(options => @@ -81,23 +85,23 @@ builder.Services.AddAuthentication(options => }); ``` -See [Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC)](xref:blazor/security/blazor-web-app-oidc) for details on the different OpenID Connect options. +For details on the different OpenID Connect options, see . -See [Mapping, customizing, and transforming claims in ASP.NET Core](xref:security/authentication/claims) for the different claims mapping possibilities. +For the different claims mapping possibilities, see . > [!NOTE] > The following namespaces are required: - -```csharp -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using Microsoft.IdentityModel.Tokens; -``` +> +> ```csharp +> using Microsoft.AspNetCore.Authentication.Cookies; +> using Microsoft.AspNetCore.Authentication.OpenIdConnect; +> using Microsoft.IdentityModel.Protocols.OpenIdConnect; +> using Microsoft.IdentityModel.Tokens; +> ``` ### Setup the configuration properties -Add the OpenID Connect client settings to the application configuration properties. The settings must match the client configuration in the OpenID Connect server. No secrets should be persisted in application settings where they might get accidently checked in. Secrets should be stored in a secure location like Azure Key Vault in production environments or in user secrets in a development environment. See [App Secrets](xref:security/app-secrets). +Add the OpenID Connect client settings to the application configuration properties. The settings must match the client configuration in the OpenID Connect server. No secrets should be persisted in application settings where they might get accidentally checked in. Secrets should be stored in a secure location like Azure Key Vault in production environments or in user secrets in a development environment. For more informaiton, see . ```json "OpenIDConnectSettings": { @@ -109,9 +113,20 @@ Add the OpenID Connect client settings to the application configuration properti }, ``` +### Signed-out callback path configuration + +The (configuration key: "`SignedOutCallbackPath`") is the request path within the app's base path intercepted by the OpenID Connect handler where the user agent is first returned after signing out from the identity provider. The sample app doesn't set a value for the path because the default value of "`/signout-callback-oidc`" is used. After intercepting the request, the OpenID Connect handler redirects to the or , if specified. + +Configure the signed-out callback path in the app's OIDC provider registration. In the following example, the `{PORT}` placeholder is the app's port: + +> :::no-loc text="https://localhost:{PORT}/signout-callback-oidc"::: + +> [!NOTE] +> When using Microsoft Entra ID, set the path in the **Web** platform configuration's **Redirect URI** entries in the Entra or Azure portal. A port isn't required for `localhost` addresses when using Entra. Most other OIDC providers require the correct port. If you don't add the signed-out callback path URI to the app's registration in Entra, Entra refuses to redirect the user back to the app and merely asks them to close their browser window. + ### Update the ASP.NET Core pipeline method in the program class. -The UseRouting must be implemented before the UseAuthorization method. +The `UseRouting` method must be implemented before the `UseAuthorization` method. ```csharp app.UseHttpsRedirection(); @@ -126,13 +141,13 @@ app.MapRazorPages(); ### Force authorization -Add the Authorize attribute to the protected razor pages, for example the Index.cshtml.cs file +Add the [`[Authorize]` attribute](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute) to the protected Razor pages: ```csharp [Authorize] ``` -A better way would be to force the whole application to be authorized and opt out for unsecure pages +A better approach is to force authorization for the whole app and opt out for unsecure pages: ```csharp var requireAuthPolicy = new AuthorizationPolicyBuilder() @@ -143,11 +158,11 @@ builder.Services.AddAuthorizationBuilder() .SetFallbackPolicy(requireAuthPolicy); ``` -### Add a new Logout.cshtml and SignedOut.cshtml Razor page to the project +### Add a new `Logout.cshtml` and `SignedOut.cshtml` Razor pages to the project -A logout is required to sign-out both the cookie session and the OpenID Connect session. The whole application needs to redirect to the OpenID Connect server to sign-out. After a successful sign-out, the application will open the RedirectUri route. +A logout is required to sign out both the cookie session and the OpenID Connect session. The whole app needs to redirect to the OpenID Connect server to sign out. After a successful sign out, the app opens the `RedirectUri` route. -Implement a default sign-out page and change the Logout razor page code with this: +Implement a default sign-out page and change the `Logout` razor page code to the following: ```csharp [Authorize] @@ -167,7 +182,7 @@ public class LogoutModel : PageModel } ``` -The `SignedOut.cshtml` requires the AllowAnonymous attribute. +The `SignedOut.cshtml` requires the [`[AllowAnonymous]` attribute](xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute): ```csharp [AllowAnonymous] @@ -179,13 +194,13 @@ public class SignedOutModel : PageModel } ``` -### Implement Login Page +### Implement `Login` page -A Login Razor Page can also be implemented to call the **ChallengeAsync** directly with the required AuthProperties. This is not required if the whole web application requires authentication and the default Challenge is used. +A `Login` Razor page can also be implemented to call the `ChallengeAsync` directly with the required `AuthProperties`. This isn't required if the web app requires authentication and the default challenge is used. -The `login.cshtml` requires the AllowAnonymous attribute. +The `Login.cshtml` page requires the [`[AllowAnonymous]` attribute](xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute): -``` +```cshtml using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -229,7 +244,7 @@ public class LoginModel : PageModel ``` -### Add a login, logout button for the user. +### Add a login and logout button for the user ``` @if (Context.User.Identity!.IsAuthenticated) @@ -254,7 +269,7 @@ else The OpenID Connect options can be used to map claims, implement handlers or even save the tokens in the session for later usage. -The **Scope** option can be used to request different claims or a refresh token which is sent as information to the OpenID Connect server. Requesting the **offline_access** is asking the server to return a reference token which can be used to refresh the session without authenticating the user of the application again. +The `Scope` option can be used to request different claims or a refresh token which is sent as information to the OpenID Connect server. Requesting the `offline_access` is asking the server to return a reference token which can be used to refresh the session without authenticating the user of the application again. ```csharp services.AddAuthentication(options => @@ -299,33 +314,33 @@ Microsoft has multiple identity providers and OpenID Connect implementations. Mi * Microsoft Entra External ID * Azure AD B2C -If authenticating using one of the Microsoft identity providers in ASP.NET Core, it is recommended to use the [Microsoft.Identity.Web](https://github.com/AzureAD/microsoft-identity-web) Nuget packages. +If authenticating using one of the Microsoft identity providers in ASP.NET Core, it is recommended to use the [`Microsoft.Identity.Web`](https://github.com/AzureAD/microsoft-identity-web) Nuget packages. -The Microsoft.Identity.Web Nuget packages is a Microsoft specific client built on top on the ASP.NET Core OpenID Connect client with some changes to the default client. +The `Microsoft.Identity.Web` Nuget packages is a Microsoft specific client built on top on the ASP.NET Core OpenID Connect client with some changes to the default client. ## Using third party OpenID Connect provider clients -Many OpenID Connect server implementations create Nuget packages which are optimized for the same OpenID Connect implementation. These packages implement the OpenID Connect client specifics with the extras required by the specific OpenID Connect server. Microsoft.Identity.Web is one example of this. +Many OpenID Connect server implementations create Nuget packages which are optimized for the same OpenID Connect implementation. These packages implement the OpenID Connect client specifics with the extras required by the specific OpenID Connect server. `Microsoft.Identity.Web` is one example of this. -If implementing multiple OpenID Connect clients from different OpenID Connect servers in a single application, it is normally better to revert to the default ASP.NET Core implementation as the different clients overwrite some options which affect the other clients. +If implementing multiple OpenID Connect clients from different OpenID Connect servers in a single application, it's normally better to revert to the default ASP.NET Core implementation as the different clients overwrite some options which affect the other clients. [OpenIddict Web providers](https://documentation.openiddict.com/integrations/web-providers) is a client implementation which supports many different server implementations. -[IdentityModel](https://github.com/IdentityModel/IdentityModel) is a .NET standard helper library for claims-based identity, OAuth 2.0 and OpenID Connect. This can also be used to help with the client implementation. +[`IdentityModel`](https://github.com/IdentityModel/IdentityModel) is a .NET standard helper library for claims-based identity, OAuth 2.0 and OpenID Connect. This can also be used to help with the client implementation. ## Backend for frontend (BFF) security architecture -It is no longer recommended to implement OpenID Connect public clients for any web applications. +It is no longer recommended to implement OpenID Connect public clients for any web apps. -See the [draft OAuth 2.0 for Browser-Based Applications](https://datatracker.ietf.org/doc/draft-ietf-oauth-browser-based-apps/) for further details. +For more information, see the [draft OAuth 2.0 for Browser-Based Applications](https://datatracker.ietf.org/doc/draft-ietf-oauth-browser-based-apps/). -If implementing **web** applications which have no independent backend, it is recommended to use the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends) security architecture. This pattern can be implemented in different ways, but the authentication is always implemented in the backend and no sensitive data is sent to the web client for further authorization or authentication flows. +If implementing **web** applications which have no independent backend, we recommend using the [Backend for Frontend (BFF) pattern](/azure/architecture/patterns/backends-for-frontends) security architecture. This pattern can be implemented in different ways, but the authentication is always implemented in the backend, and no sensitive data is sent to the web client for further authorization or authentication flows. ## Advanced features, standards, extending the OIDC client ### Logging -Debugging OpenID Connect clients can be hard. Personally identifiable information (PII) data is not logged by default. If debugging in development mode, the ** IdentityModelEventSource.ShowPII** can be used to log sensitive personal data. This should never by deployed to productive servers. +Debugging OpenID Connect clients can be hard. Personally identifiable information (PII) data is not logged by default. If debugging in development mode, the `IdentityModelEventSource.ShowPII` can be used to log sensitive personal data. Don't deploy an app with `IdentityModelEventSource.ShowPII` to productive servers. ```csharp //using ... @@ -345,33 +360,26 @@ IdentityModelEventSource.ShowPII = true; app.Run(); ``` -See [Logging](xref:fundamentals/logging/index?view=aspnetcore-8.0#configure-logging) for further information on logging. +For more information, see [Logging](xref:fundamentals/logging/index#configure-logging). > [!NOTE] > You may want to lower the configured log level to see all the required logs. ### OIDC and OAuth Parameter Customization -The OAuth and OIDC authentication handlers [`AdditionalAuthorizationParameters`](https://source.dot.net/#Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs,ddb988460467cfbf) option allows customization of authorization message parameters that are usually included as part of the redirect query string. +The OAuth and OIDC authentication handlers () option allows customization of authorization message parameters that are usually included as part of the redirect query string. ## Map claims from OpenID Connect -Refer to the following document: - -[Mapping, customizing, and transforming claims in ASP.NET Core](xref:security/authentication/claims) +For more information, see . ## Blazor OpenID Connect -Refer to the following document: - -[Secure an ASP.NET Core Blazor Web App with OpenID Connect (OIDC)](xref:blazor/security/blazor-web-app-oidc) +For more information, see . ## Standards -[OpenID Connect 1.0](https://openid.net/specs/openid-connect-core-1_0-final.html) - -[Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636) - -[The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749) - -[OAuth 2.0 Pushed Authorization Requests (PAR) RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126) +* [OpenID Connect 1.0](https://openid.net/specs/openid-connect-core-1_0-final.html) +* [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636) +* [The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749) +* [OAuth 2.0 Pushed Authorization Requests (PAR) RFC 9126](https://datatracker.ietf.org/doc/html/rfc9126) diff --git a/aspnetcore/security/cross-site-scripting.md b/aspnetcore/security/cross-site-scripting.md index 26f0982b531a..e6c19509c965 100644 --- a/aspnetcore/security/cross-site-scripting.md +++ b/aspnetcore/security/cross-site-scripting.md @@ -22,14 +22,10 @@ To prevent XSS attacks, web APIs should implement input validation and output en At a basic level, XSS works by tricking your application into inserting a `