diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index faaf355d258b..133dd8d65645 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -1322,6 +1322,41 @@ "source_path": "aspnetcore/blazor/components/prerendering-and-integration.md", "redirect_url": "/aspnet/core/blazor/components/integration", "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/webassembly/standalone-with-identity.md", + "redirect_url": "/aspnet/core/blazor/security/webassembly/standalone-with-identity/", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/account-confirmation-and-password-recovery.md", + "redirect_url": "/aspnet/core/blazor/security/account-confirmation-and-password-recovery", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/interactive-server-side-rendering.md", + "redirect_url": "/aspnet/core/blazor/security/interactive-server-side-rendering", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/qrcodes-for-authenticator-apps.md", + "redirect_url": "/aspnet/core/blazor/security/qrcodes-for-authenticator-apps", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/static-server-side-rendering.md", + "redirect_url": "/aspnet/core/blazor/security/static-server-side-rendering", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/additional-scenarios.md", + "redirect_url": "/aspnet/core/blazor/security/additional-scenarios", + "redirect_document_id": false + }, + { + "source_path": "aspnetcore/blazor/security/server/index.md", + "redirect_url": "/aspnet/core/blazor/security/", + "redirect_document_id": false } ] } diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index a446f9155d76..35e820337b86 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -684,7 +684,7 @@ builder.Services.AddHttpClient(...) :::moniker range=">= aspnetcore-8.0" -For a demonstration, see . +For a demonstration, see . :::moniker-end @@ -901,7 +901,7 @@ For guidance on mitigating overposting attacks, see : Includes coverage on using to make secure web API requests. +* : Includes coverage on using to make secure web API requests. * * * [Kestrel HTTPS endpoint configuration](xref:fundamentals/servers/kestrel/endpoints) diff --git a/aspnetcore/blazor/components/integration.md b/aspnetcore/blazor/components/integration.md index 2f5bd5e3b838..93e4d0a1a001 100644 --- a/aspnetcore/blazor/components/integration.md +++ b/aspnetcore/blazor/components/integration.md @@ -557,7 +557,7 @@ To resolve the problem, use ***either*** of the following approaches: * [Authentication and authorization: General aspects](xref:blazor/security/index#aspnet-core-blazor-authentication-and-authorization) * [Handle Errors: Prerendering](xref:blazor/fundamentals/handle-errors#prerendering) * [Host and deploy: Blazor Server](xref:blazor/host-and-deploy/server) -* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) +* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) * is executed *twice* when prerendering: [Handle asynchronous navigation events with `OnNavigateAsync`](xref:blazor/fundamentals/routing#handle-asynchronous-navigation-events-with-onnavigateasync) :::moniker-end @@ -1076,7 +1076,7 @@ To resolve the problem, use ***either*** of the following approaches: * [Authentication and authorization: General aspects](xref:blazor/security/index#aspnet-core-blazor-authentication-and-authorization) * [Handle Errors: Prerendering](xref:blazor/fundamentals/handle-errors#prerendering) * [Host and deploy: Blazor Server](xref:blazor/host-and-deploy/server) -* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) +* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) :::moniker-end @@ -1508,7 +1508,7 @@ To resolve the problem, use ***either*** of the following approaches: * [Authentication and authorization: General aspects](xref:blazor/security/index#aspnet-core-blazor-authentication-and-authorization) * [Handle Errors: Prerendering](xref:blazor/fundamentals/handle-errors#prerendering) * [Host and deploy: Blazor Server](xref:blazor/host-and-deploy/server) -* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) +* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) :::moniker-end @@ -1938,6 +1938,6 @@ To resolve the problem, use ***either*** of the following approaches: * [Authentication and authorization: General aspects](xref:blazor/security/index#aspnet-core-blazor-authentication-and-authorization) * [Handle Errors: Prerendering](xref:blazor/fundamentals/handle-errors#prerendering) * [Host and deploy: Blazor Server](xref:blazor/host-and-deploy/server) -* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) +* [Threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) :::moniker-end diff --git a/aspnetcore/blazor/components/prerender.md b/aspnetcore/blazor/components/prerender.md index 03d1f6f3416b..241e1cbe38cd 100644 --- a/aspnetcore/blazor/components/prerender.md +++ b/aspnetcore/blazor/components/prerender.md @@ -155,8 +155,8 @@ Prerendering guidance is organized in the Blazor documentation by subject matter * [Prerendering when integrating components into Razor Pages and MVC apps](xref:blazor/components/integration) * Authentication and authorization - * [Server-side threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) - * [Server-side unauthorized content display while prerendering with a custom `AuthenticationStateProvider`](xref:blazor/security/server/index#unauthorized-content-display-while-prerendering-with-a-custom-authenticationstateprovider) + * [Server-side threat mitigation: Cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) + * [Server-side unauthorized content display while prerendering with a custom `AuthenticationStateProvider`](xref:blazor/security/index#unauthorized-content-display-while-prerendering-with-a-custom-authenticationstateprovider) * [Blazor WebAssembly rendered component authentication with prerendering](xref:blazor/security/webassembly/additional-scenarios#prerendering-with-authentication) * [State management: Handle prerendering](xref:blazor/state-management#handle-prerendering): Besides the *Handle prerendering* section, several of the article's other sections include remarks on prerendering. diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index 7d7d5a587f00..df23da6937ba 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -960,7 +960,7 @@ To address this scenario, inject the service in a new imports file placed in the * WebSocket compression * - * + * * * [Cascading values/parameters and render mode boundaries](xref:blazor/components/cascading-values-and-parameters#cascading-valuesparameters-and-render-mode-boundaries): Also see the [Root-level cascading parameters](xref:blazor/components/cascading-values-and-parameters#root-level-cascading-parameters) section earlier in the article. * diff --git a/aspnetcore/blazor/file-uploads.md b/aspnetcore/blazor/file-uploads.md index d40a57babd55..bd021fb4e90b 100644 --- a/aspnetcore/blazor/file-uploads.md +++ b/aspnetcore/blazor/file-uploads.md @@ -49,7 +49,7 @@ To read data from a user-selected file, call enforces a maximum size in bytes of its . Reading one file or multiple files larger than 500 KB results in an exception. This limit prevents developers from accidentally reading large files into memory. The `maxAllowedSize` parameter of can be used to specify a larger size if required. -If you need access to a that represents the file's bytes, use . Avoid reading the incoming file stream directly into memory all at once. For example, don't copy all of the file's bytes into a or read the entire stream into a byte array all at once. These approaches can result in degraded app performance and potential [Denial of Service (DoS)](xref:blazor/security/server/interactive-server-side-rendering#denial-of-service-dos-attacks) risk, especially for server-side components. Instead, consider adopting either of the following approaches: +If you need access to a that represents the file's bytes, use . Avoid reading the incoming file stream directly into memory all at once. For example, don't copy all of the file's bytes into a or read the entire stream into a byte array all at once. These approaches can result in degraded app performance and potential [Denial of Service (DoS)](xref:blazor/security/interactive-server-side-rendering#denial-of-service-dos-attacks) risk, especially for server-side components. Instead, consider adopting either of the following approaches: * Copy the stream directly to a file on disk without reading it into memory. Note that Blazor apps executing code on the server aren't able to access the client's file system directly. * Upload files from the client directly to an external service. For more information, see the [Upload files to an external service](#upload-files-to-an-external-service) section. diff --git a/aspnetcore/blazor/fundamentals/dependency-injection.md b/aspnetcore/blazor/fundamentals/dependency-injection.md index 0af6dc833731..53018cf8915f 100644 --- a/aspnetcore/blazor/fundamentals/dependency-injection.md +++ b/aspnetcore/blazor/fundamentals/dependency-injection.md @@ -612,7 +612,7 @@ public static class CircuitServicesServiceCollectionExtensions Access the circuit-scoped services by injecting the `CircuitServicesAccessor` where it's needed. -For an example that shows how to access the from a set up using , see . +For an example that shows how to access the from a set up using , see . :::moniker-end diff --git a/aspnetcore/blazor/fundamentals/index.md b/aspnetcore/blazor/fundamentals/index.md index a84a5333cac0..1bf538ba6cb1 100644 --- a/aspnetcore/blazor/fundamentals/index.md +++ b/aspnetcore/blazor/fundamentals/index.md @@ -178,6 +178,22 @@ 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" + +Samples apps in the repository: + +* Blazor Web App +* Blazor WebAssembly +* 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 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) () + +:::moniker-end + :::moniker range="< aspnetcore-8.0" The sample repo contains two types of samples: diff --git a/aspnetcore/blazor/fundamentals/signalr.md b/aspnetcore/blazor/fundamentals/signalr.md index eda5461f5a28..4987489b1b83 100644 --- a/aspnetcore/blazor/fundamentals/signalr.md +++ b/aspnetcore/blazor/fundamentals/signalr.md @@ -46,7 +46,7 @@ Use to `true`, which reduces the [vulnerability of the app to attack](xref:blazor/security/server/interactive-server-side-rendering#interactive-server-components-with-websocket-compression-enabled) but may result in reduced performance: +Disable compression by setting to `true`, which reduces the [vulnerability of the app to attack](xref:blazor/security/interactive-server-side-rendering#interactive-server-components-with-websocket-compression-enabled) but may result in reduced performance: ```csharp builder.MapRazorComponents() @@ -78,7 +78,7 @@ builder.MapRazorComponents() > > Additional options include specifying one or more host sources and scheme sources. -For security implications, see . For more information on the `frame-ancestors` directive, see [CSP: `frame-ancestors` (MDN documentation)](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors). +For security implications, see . For more information on the `frame-ancestors` directive, see [CSP: `frame-ancestors` (MDN documentation)](https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors). :::moniker-end @@ -295,7 +295,7 @@ services.AddServerSideBlazor().AddHubOptions(options => :::moniker-end > [!WARNING] -> The default value of is 32 KB. Increasing the value may increase the risk of [Denial of Service (DoS) attacks](xref:blazor/security/server/interactive-server-side-rendering#denial-of-service-dos-attacks). +> The default value of is 32 KB. Increasing the value may increase the risk of [Denial of Service (DoS) attacks](xref:blazor/security/interactive-server-side-rendering#denial-of-service-dos-attacks). > > Blazor relies on set to 1, which is the default value. For more information, see [MaximumParallelInvocationsPerClient > 1 breaks file upload in Blazor Server mode (`dotnet/aspnetcore` #53951)](https://github.com/dotnet/aspnetcore/issues/53951). @@ -408,7 +408,7 @@ builder.Services.AddRazorComponents().AddInteractiveServerComponents() .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024); ``` -Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/server/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. +Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. A better option for reading large payloads is to send the content in smaller chunks and process the payload as a . This can be used when reading large JavaScript (JS) interop JSON payloads or if JS interop data is available as raw bytes. For an example that demonstrates sending large binary payloads in server-side apps that uses techniques similar to the [`InputFile` component](xref:blazor/file-uploads), see the [Binary Submit sample app](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/BinarySubmit) and the [Blazor `InputLargeTextArea` Component Sample](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/InputLargeTextArea). @@ -427,7 +427,7 @@ builder.Services.AddServerSideBlazor() .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024); ``` -Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/server/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. +Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. A better option for reading large payloads is to send the content in smaller chunks and process the payload as a . This can be used when reading large JavaScript (JS) interop JSON payloads or if JS interop data is available as raw bytes. For an example that demonstrates sending large binary payloads in Blazor Server that uses techniques similar to the [`InputFile` component](xref:blazor/file-uploads), see the [Binary Submit sample app](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/BinarySubmit) and the [Blazor `InputLargeTextArea` Component Sample](https://github.com/aspnet/samples/tree/main/samples/aspnetcore/blazor/InputLargeTextArea). @@ -446,7 +446,7 @@ services.AddServerSideBlazor() .AddHubOptions(options => options.MaximumReceiveMessageSize = 64 * 1024); ``` -Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/server/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. +Increasing the SignalR incoming message size limit comes at the cost of requiring more server resources, and it increases the risk of [Denial of Service (DoS) attacks](xref:blazor/security/interactive-server-side-rendering#denial-of-service-dos-attacks). Additionally, reading a large amount of content in to memory as strings or byte arrays can also result in allocations that work poorly with the garbage collector, resulting in additional performance penalties. :::moniker-end @@ -760,7 +760,7 @@ builder.Services.AddIdleCircuitHandler(options => Circuit activity handlers also provide an approach for accessing scoped Blazor services from other non-Blazor dependency injection (DI) scopes. For more information and examples, see: * -* +* :::moniker-end @@ -1444,7 +1444,7 @@ When a circuit ends because a user has disconnected and the framework is cleanin ## Server-side circuit handler to capture users for custom services -Use a to capture a user from the and set that user in a service. For more information and example code, see . +Use a to capture a user from the and set that user in a service. For more information and example code, see . :::moniker range=">= aspnetcore-8.0" @@ -1465,9 +1465,9 @@ Use a to c * * Server-side security documentation * - * - * - * + * + * + * * [Server-side reconnection events and component lifecycle events](xref:blazor/components/lifecycle#blazor-server-reconnection-events) * [What is Azure SignalR Service?](/azure/azure-signalr/signalr-overview) * [Performance guide for Azure SignalR Service](/azure/azure-signalr/signalr-concept-performance) diff --git a/aspnetcore/blazor/fundamentals/startup.md b/aspnetcore/blazor/fundamentals/startup.md index 99ba9ade81fb..dc4128f17a88 100644 --- a/aspnetcore/blazor/fundamentals/startup.md +++ b/aspnetcore/blazor/fundamentals/startup.md @@ -240,7 +240,7 @@ For examples of JS initializers, see the following resources: * * (*`quoteContainer2` example*) * (*Custom clipboard paste event example*) -* +* * [Basic Test App in the ASP.NET Core GitHub repository (`BasicTestApp.lib.module.js`)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/test/testassets/BasicTestApp/wwwroot/BasicTestApp.lib.module.js) :::moniker-end diff --git a/aspnetcore/blazor/host-and-deploy/server.md b/aspnetcore/blazor/host-and-deploy/server.md index 87b1c861bbe5..5837da0ee65b 100644 --- a/aspnetcore/blazor/host-and-deploy/server.md +++ b/aspnetcore/blazor/host-and-deploy/server.md @@ -35,8 +35,8 @@ When considering the scalability of a single server (scale up), the memory avail For guidance on building secure and scalable server-side Blazor apps, see the following resources: -* -* +* +* Each circuit uses approximately 250 KB of memory for a minimal *Hello World*-style app. The size of a circuit depends on the app's code and the state maintenance requirements associated with each component. We recommend that you measure resource demands during development for your app and infrastructure, but the following baseline can be a starting point in planning your deployment target: If you expect your app to support 5,000 concurrent users, consider budgeting at least 1.3 GB of server memory to the app (or ~273 KB per user). diff --git a/aspnetcore/blazor/hybrid/security/security-considerations.md b/aspnetcore/blazor/hybrid/security/security-considerations.md index ed7c191ce6f9..0b54f97faa95 100644 --- a/aspnetcore/blazor/hybrid/security/security-considerations.md +++ b/aspnetcore/blazor/hybrid/security/security-considerations.md @@ -39,7 +39,7 @@ If your app must reference content from an external origin, we recommend that yo * Institute a [Content Security Policy (CSP)](https://developer.mozilla.org/docs/Web/HTTP/CSP). * Perform [subresource integrity](https://developer.mozilla.org/docs/Web/Security/Subresource_Integrity) checks. -Even if all of the resources are packed into the app and don't load from any external origin, remain cautious about problems in the resources' code that run inside the Web View, as the resources might have vulnerabilities that could allow [cross-site scripting (XSS)](xref:blazor/security/server/interactive-server-side-rendering#cross-site-scripting-xss) attacks. +Even if all of the resources are packed into the app and don't load from any external origin, remain cautious about problems in the resources' code that run inside the Web View, as the resources might have vulnerabilities that could allow [cross-site scripting (XSS)](xref:blazor/security/interactive-server-side-rendering#cross-site-scripting-xss) attacks. In general, the Blazor framework protects against XSS by dealing with HTML in safe ways. However, some programming patterns allow Razor components to inject raw HTML into rendered output, such as rendering content from an untrusted source. For example, rendering HTML content directly from a database should be avoided. Additionally, JavaScript libraries used by the app might manipulate HTML in unsafe ways to inadvertently or deliberately render unsafe output. diff --git a/aspnetcore/blazor/includes/compression-with-untrusted-data.md b/aspnetcore/blazor/includes/compression-with-untrusted-data.md index 200001daa568..8f1692bd4f44 100644 --- a/aspnetcore/blazor/includes/compression-with-untrusted-data.md +++ b/aspnetcore/blazor/includes/compression-with-untrusted-data.md @@ -1,6 +1,6 @@ :::moniker range=">= aspnetcore-9.0" > [!WARNING] -> With compression, which is enabled by default, avoid creating secure (authenticated/authorized) interactive server-side components that render data from untrusted sources. Untrusted sources include route parameters, query strings, data from JS interop, and any other source of data that a third-party user can control (databases, external services). For more information, see and . +> With compression, which is enabled by default, avoid creating secure (authenticated/authorized) interactive server-side components that render data from untrusted sources. Untrusted sources include route parameters, query strings, data from JS interop, and any other source of data that a third-party user can control (databases, external services). For more information, see and . :::moniker-end diff --git a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md index 40f26506f402..f974c5cae4e7 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md +++ b/aspnetcore/blazor/javascript-interoperability/call-dotnet-from-javascript.md @@ -1746,4 +1746,4 @@ For more information, see (*JavaScript interop* section) -* [Threat mitigation: .NET methods invoked from the browser](xref:blazor/security/server/interactive-server-side-rendering#net-methods-invoked-from-the-browser) +* [Threat mitigation: .NET methods invoked from the browser](xref:blazor/security/interactive-server-side-rendering#net-methods-invoked-from-the-browser) diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index 957450b76d1a..5b8ec9a573d3 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -1112,7 +1112,7 @@ In the preceding example: Although a common cause of JS interop failures are network failures with server-side components, per-invocation timeouts can be set for JS interop calls for client-side components. Although no Blazor-SignalR circuit exists for a client-side component, JS interop calls might fail for other reasons that apply. -For more information on resource exhaustion, see . +For more information on resource exhaustion, see . ## Avoid circular object references @@ -1740,4 +1740,4 @@ For more information, see (*JavaScript interop* section) -* [Threat mitigation: JavaScript functions invoked from .NET](xref:blazor/security/server/interactive-server-side-rendering#javascript-functions-invoked-from-net) +* [Threat mitigation: JavaScript functions invoked from .NET](xref:blazor/security/interactive-server-side-rendering#javascript-functions-invoked-from-net) diff --git a/aspnetcore/blazor/javascript-interoperability/index.md b/aspnetcore/blazor/javascript-interoperability/index.md index a970d325670a..d4b7adc104d1 100644 --- a/aspnetcore/blazor/javascript-interoperability/index.md +++ b/aspnetcore/blazor/javascript-interoperability/index.md @@ -36,7 +36,7 @@ Further JS interop guidance is provided in the following articles: blazor/includes/compression-with-untrusted-data.md because the text is used in a warning format in two articles. --> -With compression, which is enabled by default, avoid creating secure (authenticated/authorized) interactive server-side components that render data from untrusted sources. Untrusted sources include route parameters, query strings, data from JS interop, and any other source of data that a third-party user can control (databases, external services). For more information, see and . +With compression, which is enabled by default, avoid creating secure (authenticated/authorized) interactive server-side components that render data from untrusted sources. Untrusted sources include route parameters, query strings, data from JS interop, and any other source of data that a third-party user can control (databases, external services). For more information, see and . :::moniker-end diff --git a/aspnetcore/blazor/javascript-interoperability/static-server-rendering.md b/aspnetcore/blazor/javascript-interoperability/static-server-rendering.md index 7db454ab0b13..469e0a14cf51 100644 --- a/aspnetcore/blazor/javascript-interoperability/static-server-rendering.md +++ b/aspnetcore/blazor/javascript-interoperability/static-server-rendering.md @@ -91,4 +91,4 @@ To monitor changes in specific DOM elements, use the [`MutationObserver`](https: ## Example implementation without using an RCL -The approach described in this article can be implemented directly in a Blazor Web App without using a Razor class library (RCL). For an example, see . +The approach described in this article can be implemented directly in a Blazor Web App without using a Razor class library (RCL). For an example, see . diff --git a/aspnetcore/blazor/security/server/account-confirmation-and-password-recovery.md b/aspnetcore/blazor/security/account-confirmation-and-password-recovery.md similarity index 74% rename from aspnetcore/blazor/security/server/account-confirmation-and-password-recovery.md rename to aspnetcore/blazor/security/account-confirmation-and-password-recovery.md index d6284841360b..1218060ef897 100644 --- a/aspnetcore/blazor/security/server/account-confirmation-and-password-recovery.md +++ b/aspnetcore/blazor/security/account-confirmation-and-password-recovery.md @@ -5,7 +5,7 @@ description: Learn how to configure an ASP.NET Core Blazor Web App with email co ms.author: riande monikerRange: '>= aspnetcore-8.0' ms.date: 11/12/2024 -uid: blazor/security/server/account-confirmation-and-password-recovery +uid: blazor/security/account-confirmation-and-password-recovery --- # Account confirmation and password recovery in ASP.NET Core Blazor @@ -13,6 +13,9 @@ uid: blazor/security/server/account-confirmation-and-password-recovery This article explains how to configure an ASP.NET Core Blazor Web App with email confirmation and password recovery. +> [!NOTE] +> This article only applies to Blazor Web Apps. To implement email confirmation and password recovery for standalone Blazor WebAssembly apps with ASP.NET Core Identity, see . + ## Namespace The app's namespace used by the example in this article is `BlazorSample`. Update the code examples to use the namespace of your app. @@ -21,9 +24,9 @@ The app's namespace used by the example in this article is `BlazorSample`. Updat In this article, [Mailchimp's Transactional API](https://mailchimp.com/developer/transactional/api/) is used via [Mandrill.net](https://www.nuget.org/packages/Mandrill.net) to send email. We recommend using an email service to send email rather than SMTP. SMTP is difficult to configure and secure properly. Whichever email service you use, access their guidance for .NET apps, create an account, configure an API key for their service, and install any NuGet packages required. -Create a class to fetch the secure email API key. The example in this article uses a class named `AuthMessageSenderOptions` with a `EmailAuthKey` property to hold the key. +Create a class to hold the secret email provider API key. The example in this article uses a class named `AuthMessageSenderOptions` with an `EmailAuthKey` property to hold the key. -`AuthMessageSenderOptions`: +`AuthMessageSenderOptions.cs`: ```csharp namespace BlazorSample; @@ -42,19 +45,31 @@ builder.Services.Configure(builder.Configuration); ## Configure a user secret for the provider's security key -Set the key with the [Secret Manager tool](xref:security/app-secrets). In the following example, the key name is `EmailAuthKey`, and the key is represented by the `{KEY}` placeholder. In a command shell, navigate to the app's root folder and execute the following command with the API key: +If the project has already been initialized for the [Secret Manager tool](xref:security/app-secrets), it will already have an app secrets identifier (``) in its project file (`.csproj`). In Visual Studio, you can tell if the app secrets ID is present by looking at the **Properties** panel when the project is selected in **Solution Explorer**. If the app hasn't been initialized, execute the following command in a command shell opened to the project's directory. In Visual Studio, you can use the Developer PowerShell command prompt. + +```dotnetcli +dotnet user-secrets init +``` + +Set the API key with the Secret Manager tool. In the following example, the key name is `EmailAuthKey` to match `AuthMessageSenderOptions.EmailAuthKey`, and the key is represented by the `{KEY}` placeholder. Execute the following command with the API key: ```dotnetcli dotnet user-secrets set "EmailAuthKey" "{KEY}" ``` +If using Visual Studio, you can confirm the secret is set by right-clicking the server project in **Solution Explorer** and selecting **Manage User Secrets**. + For more information, see . [!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)] ## Implement `IEmailSender` -Implement `IEmailSender` for the provider. The following example is based on Mailchimp's Transactional API using [Mandrill.net](https://www.nuget.org/packages/Mandrill.net). For a different provider, refer to their documentation on how to implement sending a message in the `Execute` method. +The following example is based on Mailchimp's Transactional API using [Mandrill.net](https://www.nuget.org/packages/Mandrill.net). For a different provider, refer to their documentation on how to implement sending an email message. + +Add the [Mandrill.net](https://www.nuget.org/packages/Mandrill.net) NuGet package to the project. + +Add the following `EmailSender` class to implement . In the following example, `ApplicationUser` is an . The message HTML markup can be further customized. As long as the `message` passed to `MandrillMessage` starts with the `<` character, the Mandrill.net API assumes that the message body is composed in HTML. `Components/Account/EmailSender.cs`: @@ -74,18 +89,20 @@ public class EmailSender(IOptions optionsAccessor, public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value; - public Task SendConfirmationLinkAsync(ApplicationUser user, string email, - string confirmationLink) => SendEmailAsync(email, "Confirm your email", - "Please confirm your account by " + - $"clicking here."); + public Task SendConfirmationLinkAsync(AppUser user, string email, + string confirmationLink) => SendEmailAsync(email, "Confirm your email", + "Please confirm your account by " + + $"clicking here."); - public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, - string resetLink) => SendEmailAsync(email, "Reset your password", - $"Please reset your password by clicking here."); + public Task SendPasswordResetLinkAsync(AppUser user, string email, + string resetLink) => SendEmailAsync(email, "Reset your password", + "Please reset your password by " + + $"clicking here."); - public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, - string resetCode) => SendEmailAsync(email, "Reset your password", - $"Please reset your password using the following code: {resetCode}"); + public Task SendPasswordResetCodeAsync(AppUser user, string email, + string resetCode) => SendEmailAsync(email, "Reset your password", + "Please reset your password " + + $"using the following code:
{resetCode}"); public async Task SendEmailAsync(string toEmail, string subject, string message) { @@ -111,7 +128,7 @@ public class EmailSender(IOptions optionsAccessor, ``` > [!NOTE] -> Body content for messages might require special encoding for the email service provider. If links in the message body can't be followed, consult the service provider's documentation. +> Body content for messages might require special encoding for the email service provider. If links in the message body can't be followed in the email message, consult the service provider's documentation to troubleshoot the problem. ## Configure app to support email @@ -152,6 +169,13 @@ Also in the `RegisterConfirmation` component, remove the Razor markup and code f } ``` +## Enable account confirmation after a site has users + +Enabling account confirmation on a site with users locks out all the existing users. Existing users are locked out because their accounts aren't confirmed. To work around existing user lockout, use one of the following approaches: + +* Update the database to mark all existing users as confirmed. +* Confirm existing users. For example, batch-send emails with confirmation links. + ## Email and activity timeout The default inactivity timeout is 14 days. The following code sets the inactivity timeout to five days with sliding expiration: @@ -172,7 +196,7 @@ builder.Services.Configure(options => options.TokenLifespan = TimeSpan.FromHours(3)); ``` -The built in Identity user tokens ([AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Extensions.Core/src/TokenOptions.cs)) have a [one day timeout](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Core/src/DataProtectionTokenProviderOptions.cs). +The built-in Identity user tokens ([AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Extensions.Core/src/TokenOptions.cs)) have a [one day timeout](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Core/src/DataProtectionTokenProviderOptions.cs). [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] @@ -182,7 +206,7 @@ The default token lifespan of the [Identity user tokens](https://github.com/dotn [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] -To change the email token lifespan, add a custom and : +To change the email token lifespan, add a custom and . `CustomTokenProvider.cs`: @@ -269,16 +293,6 @@ If you can't get email working: * Try another email alias on a different email provider, such as Microsoft, Yahoo, or Gmail. * Try sending to different email accounts. -> [!WARNING] -> Do **not** use production secrets in test and development. If you publish the app to Azure, set secrets as application settings in the Azure Web App portal. The configuration system is set up to read keys from environment variables. - -## Enable account confirmation after a site has users - -Enabling account confirmation on a site with users locks out all the existing users. Existing users are locked out because their accounts aren't confirmed. To work around existing user lockout, use one of the following approaches: - -* Update the database to mark all existing users as confirmed. -* Confirm existing users. For example, batch-send emails with confirmation links. - ## Additional resources * [Mandrill.net (GitHub repository)](https://github.com/feinoujc/Mandrill.net) diff --git a/aspnetcore/blazor/security/server/additional-scenarios.md b/aspnetcore/blazor/security/additional-scenarios.md similarity index 98% rename from aspnetcore/blazor/security/server/additional-scenarios.md rename to aspnetcore/blazor/security/additional-scenarios.md index 631a500e0a39..b80d187f8d8d 100644 --- a/aspnetcore/blazor/security/server/additional-scenarios.md +++ b/aspnetcore/blazor/security/additional-scenarios.md @@ -1,14 +1,14 @@ --- -title: Server-side ASP.NET Core Blazor additional security scenarios +title: ASP.NET Core server-side and Blazor Web App additional security scenarios author: guardrex -description: Learn how to configure server-side Blazor for additional security scenarios. +description: Learn how to configure server-side Blazor and Blazor Web Apps for additional security scenarios. monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 -uid: blazor/security/server/additional-scenarios +uid: blazor/security/additional-scenarios --- -# Server-side ASP.NET Core Blazor additional security scenarios +# ASP.NET Core server-side and Blazor Web App additional security scenarios [!INCLUDE[](~/includes/not-latest-version.md)] @@ -23,7 +23,7 @@ This article explains how to configure server-side Blazor for additional securit Updating this section for Blazor Web Apps is pending [Update section on passing tokens in Blazor Web Apps (`dotnet/AspNetCore.Docs` #31691)](https://github.com/dotnet/AspNetCore.Docs/issues/31691). For more information, see [Problem providing Access Token to HttpClient in Interactive Server mode (`dotnet/aspnetcore` #52390)](https://github.com/dotnet/aspnetcore/issues/52390). -For Blazor Server, view the [7.0 version of this article section](xref:blazor/security/server/additional-scenarios?view=aspnetcore-7.0#pass-tokens-to-a-server-side-blazor-app). +For Blazor Server, view the [7.0 version of this article section](xref:blazor/security/additional-scenarios?view=aspnetcore-7.0#pass-tokens-to-a-server-side-blazor-app). + +For more information on the Blazor Identity UI and guidance on integrating external logins through social websites, see [What's new with identity in .NET 8](https://devblogs.microsoft.com/dotnet/whats-new-with-identity-in-dotnet-8/#the-blazor-identity-ui). + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +### Manage authentication state in Blazor Web Apps + +*This section applies to Blazor Web Apps that adopt:* + +* Individual Accounts +* *Client-side rendering (CSR, WebAssembly-based interactivity).* + +A client-side authentication state provider is only used within Blazor and isn't integrated with the ASP.NET Core authentication system. During prerendering, Blazor respects the metadata defined on the page and uses the ASP.NET Core authentication system to determine if the user is authenticated. When a user navigates from one page to another, a client-side authentication provider is used. When the user refreshes the page (full-page reload), the client-side authentication state provider isn't involved in the authentication decision on the server. Since the user's state isn't persisted by the server, any authentication state maintained client-side is lost. + +To address this, the best approach is to perform authentication within the ASP.NET Core authentication system. The client-side authentication state provider only takes care of reflecting the user's authentication state. Examples for how to accomplish this with authentication state providers are demonstrated by the Blazor Web App project template and described below. + +:::moniker-end + +:::moniker range=">= aspnetcore-9.0" + +In the server project's `Program` file, call `AddAuthenticationStateSerialization`, which serializes the returned by the server-side using the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) (): + +```csharp +builder.Services.AddRazorComponents() + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); +``` + +The API only serializes the server-side name and role claims for access in the browser. To include all claims, set `SerializeAllClaims` to `true` in the server-side call to `AddAuthenticationStateSerialization`: + +```csharp +builder.Services.AddRazorComponents() + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization( + options => options.SerializeAllClaims = true); +``` + +In the client (`.Client`) project's `Program` file, call `AddAuthenticationStateDeserialization`, which adds an where the is deserialized from the server using `AuthenticationStateData` and the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) (). There should be a corresponding call to `AddAuthenticationStateSerialization` in the server project. + +```csharp +builder.Services.AddAuthorizationCore(); +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddAuthenticationStateDeserialization(); +``` + +:::moniker-end + +:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" + +* [`PersistingRevalidatingAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs): For Blazor Web Apps that adopt interactive server-side rendering (interactive SSR) and client-side rendering (CSR). This is a server-side that revalidates the security stamp for the connected user every 30 minutes an interactive circuit is connected. It also uses the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) to flow the authentication state to the client, which is then fixed for the lifetime of CSR. + +* [`PersistingServerAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/PersistingServerAuthenticationStateProvider.cs): For Blazor Web Apps that only adopt CSR. This is a server-side that uses the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) to flow the authentication state to the client, which is then fixed for the lifetime of CSR. + +* [`PersistentAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs): For Blazor Web Apps that adopt CSR. This is a client-side that determines the user's authentication state by looking for data persisted in the page when it was rendered on the server. This authentication state is fixed for the lifetime of CSR. If the user needs to log in or out, a full-page reload is required. This only provides a user name and email for display purposes. It doesn't include tokens that authenticate to the server when making subsequent requests, which is handled separately using a cookie that's included on `HttpClient` requests to the server. + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +:::moniker-end + +:::moniker range="< aspnetcore-8.0" + +### Scaffold Identity + +:::moniker-end + +:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0" + +For more information on scaffolding Identity into a server-side Blazor app, see . + +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +Scaffold Identity into a server-side Blazor app: + +* [Without existing authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-server-side-blazor-app-without-existing-authorization). +* [With authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-server-side-blazor-app-with-authorization). + +:::moniker-end + +### Additional claims and tokens from external providers + +To store additional claims from external providers, see . + +### Azure App Service on Linux with Identity Server + +Specify the issuer explicitly when deploying to Azure App Service on Linux with Identity Server. For more information, see . + +### Inject `AuthenticationStateProvider` for services scoped to a component + +Don't attempt to resolve within a custom scope because it results in the creation of a new instance of the that isn't correctly initialized. + +To access the within a service scoped to a component, inject the with the [`@inject` directive](xref:mvc/views/razor#inject) or the [`[Inject]` attribute](xref:Microsoft.AspNetCore.Components.InjectAttribute) and pass it to the service as a parameter. This approach ensures that the correct, initialized instance of the is used for each user app instance. + +`ExampleService.cs`: + +```csharp +public class ExampleService +{ + public async Task ExampleMethod(AuthenticationStateProvider authStateProvider) + { + var authState = await authStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + if (user.Identity is not null && user.Identity.IsAuthenticated) + { + return $"{user.Identity.Name} is authenticated."; + } + else + { + return "The user is NOT authenticated."; + } + } +} +``` + +Register the service as scoped. In a server-side Blazor app, scoped services have a lifetime equal to the duration of the client connection [circuit](xref:blazor/hosting-models#blazor-server). + +:::moniker range=">= aspnetcore-6.0" + +In the `Program` file: + +```csharp +builder.Services.AddScoped(); +``` + +:::moniker-end + +:::moniker range="< aspnetcore-6.0" + +In `Startup.ConfigureServices` of `Startup.cs`: + +```csharp +services.AddScoped(); +``` + +:::moniker-end + +In the following `InjectAuthStateProvider` component: + +* The component inherits . +* The is injected and passed to `ExampleService.ExampleMethod`. +* `ExampleService` is resolved with and , which returns the correct, initialized instance of `ExampleService` that exists for the lifetime of the user's circuit. + +`InjectAuthStateProvider.razor`: + +:::moniker range=">= aspnetcore-8.0" + +```razor +@page "/inject-auth-state-provider" +@inherits OwningComponentBase +@inject AuthenticationStateProvider AuthenticationStateProvider + +

Inject AuthenticationStateProvider Example

+ +

@message

+ +@code { + private string? message; + private ExampleService? ExampleService { get; set; } + + protected override async Task OnInitializedAsync() + { + ExampleService = ScopedServices.GetRequiredService(); + + message = await ExampleService.ExampleMethod(AuthenticationStateProvider); + } +} +``` + +:::moniker-end + +:::moniker range="< aspnetcore-8.0" + +```razor +@page "/inject-auth-state-provider" +@inject AuthenticationStateProvider AuthenticationStateProvider +@inherits OwningComponentBase + +

Inject AuthenticationStateProvider Example

+ +

@message

+ +@code { + private string? message; + private ExampleService? ExampleService { get; set; } + + protected override async Task OnInitializedAsync() + { + ExampleService = ScopedServices.GetRequiredService(); + + message = await ExampleService.ExampleMethod(AuthenticationStateProvider); + } +} +``` + +:::moniker-end + +For more information, see the guidance on in . + +### Unauthorized content display while prerendering with a custom `AuthenticationStateProvider` + +To avoid showing unauthorized content, for example content in an [`AuthorizeView` component](xref:blazor/security/index#authorizeview-component), while prerendering with a [custom `AuthenticationStateProvider`](xref:blazor/security/authentication-state#implement-a-custom-authenticationstateprovider), adopt ***one*** of the following approaches: + +* Implement for the custom to support prerendering: For an example implementation of , see the Blazor framework's implementation in [`ServerAuthenticationStateProvider.cs` (reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Endpoints/src/DependencyInjection/ServerAuthenticationStateProvider.cs). + + [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +:::moniker range=">= aspnetcore-8.0" + +* Disable prerendering: Indicate the render mode with the `prerender` parameter set to `false` at the highest-level component in the app's component hierarchy that isn't a root component. + + > [!NOTE] + > Making a root component interactive, such as the `App` component, isn't supported. Therefore, prerendering can't be disabled directly by the `App` component. + + For apps based on the Blazor Web App project template, prerendering is typically disabled where the `Routes` component is used in the `App` component (`Components/App.razor`) : + + ```razor + + ``` + + Also, disable prerendering for the `HeadOutlet` component: + + ```razor + + ``` + + You can also selectively control the render mode applied to the `Routes` component instance. For example, see . + +:::moniker-end + +:::moniker range="< aspnetcore-8.0" + +* Disable prerendering: Open the `_Host.cshtml` file and change the `render-mode` attribute of the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) to : + + ```cshtml + + ``` + +:::moniker-end + +* Authenticate the user on the server before the app starts: To adopt this approach, the app must respond to a user's initial request with the Identity-based sign-in page or view and prevent any requests to Blazor endpoints until they're authenticated. For more information, see . After authentication, unauthorized content in prerendered Razor components is only shown when the user is truly unauthorized to view the content. + +### User state management + +In spite of the word "state" in the name, isn't for storing *general user state*. only indicates the user's authentication state to the app, whether they are signed into the app and who they are signed in as. + +Authentication uses the same ASP.NET Core Identity authentication as Razor Pages and MVC apps. The user state stored for ASP.NET Core Identity flows to Blazor without adding additional code to the app. Follow the guidance in the ASP.NET Core Identity articles and tutorials for the Identity features to take effect in the Blazor parts of the app. + +For guidance on general state management outside of ASP.NET Core Identity, see . + +### Additional security abstractions + +Two additional abstractions participate in managing authentication state: + +* ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Endpoints/src/DependencyInjection/ServerAuthenticationStateProvider.cs)): An used by the Blazor framework to obtain authentication state from the server. + +* ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/Circuits/RevalidatingServerAuthenticationStateProvider.cs)): A base class for services used by the Blazor framework to receive an authentication state from the host environment and revalidate it at regular intervals. + + The default 30 minute revalidation interval can be adjusted in [`RevalidatingIdentityAuthenticationStateProvider` (`Areas/Identity/RevalidatingIdentityAuthenticationStateProvider.cs`)](https://github.com/dotnet/aspnetcore/blob/release/7.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/RevalidatingIdentityAuthenticationStateProvider.cs). The following example shortens the interval to 20 minutes: + + ```csharp + protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(20); + ``` + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +### Authentication state management at sign out + +Server-side Blazor persists user authentication state for the lifetime of the circuit, including across browser tabs. To proactively sign off a user across browser tabs when the user signs out on one tab, you must implement a ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/Circuits/RevalidatingServerAuthenticationStateProvider.cs)) with a short . + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +:::moniker range=">= aspnetcore-8.0" + +### Temporary redirection URL validity duration + +*This section applies to Blazor Web Apps.* + +Use the option to get or set the lifetime of ASP.NET Core Data Protection validity for temporary redirection URLs emitted by Blazor server-side rendering. These are only used transiently, so the lifetime only needs to be long enough for a client to receive the URL and begin navigation to it. However, it should also be long enough to allow for clock skew across servers. The default value is five minutes. + +In the following example the value is extended to seven minutes: + +```csharp +builder.Services.AddRazorComponents(options => + options.TemporaryRedirectionUrlValidityDuration = + TimeSpan.FromMinutes(7)); +``` + +:::moniker-end + +## Client-side Blazor authentication In client-side Blazor apps, client-side authentication checks can be bypassed because all client-side code can be modified by users. The same is true for all client-side app technologies, including JavaScript SPA frameworks and native apps for any operating system. @@ -938,6 +1407,12 @@ PII refers any information relating to an identified or identifiable natural per :::moniker range=">= aspnetcore-6.0" +* Server-side and Blazor Web App resources + * [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/entra/identity-platform/quickstart-v2-aspnet-core-webapp) + * [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/entra/identity-platform/quickstart-v2-aspnet-core-web-api) + * : 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. * Microsoft identity platform documentation * [Overview](/entra/identity-platform/) * [OAuth 2.0 and OpenID Connect protocols on the Microsoft identity platform](/entra/identity-platform/v2-protocols) @@ -954,6 +1429,12 @@ PII refers any information relating to an identified or identifiable natural per :::moniker range="< aspnetcore-6.0" +* Server-side Blazor resources + * [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/entra/identity-platform/quickstart-v2-aspnet-core-webapp) + * [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/entra/identity-platform/quickstart-v2-aspnet-core-web-api) + * : 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. * Microsoft identity platform documentation * [Overview](/entra/identity-platform/) * [OAuth 2.0 and OpenID Connect protocols on the Microsoft identity platform](/entra/identity-platform/v2-protocols) diff --git a/aspnetcore/blazor/security/server/interactive-server-side-rendering.md b/aspnetcore/blazor/security/interactive-server-side-rendering.md similarity index 99% rename from aspnetcore/blazor/security/server/interactive-server-side-rendering.md rename to aspnetcore/blazor/security/interactive-server-side-rendering.md index e0596753dccb..6107d84c4723 100644 --- a/aspnetcore/blazor/security/server/interactive-server-side-rendering.md +++ b/aspnetcore/blazor/security/interactive-server-side-rendering.md @@ -6,7 +6,7 @@ monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 -uid: blazor/security/server/interactive-server-side-rendering +uid: blazor/security/interactive-server-side-rendering --- # Threat mitigation guidance for ASP.NET Core Blazor interactive server-side rendering diff --git a/aspnetcore/blazor/security/server/qrcodes-for-authenticator-apps.md b/aspnetcore/blazor/security/qrcodes-for-authenticator-apps.md similarity index 99% rename from aspnetcore/blazor/security/server/qrcodes-for-authenticator-apps.md rename to aspnetcore/blazor/security/qrcodes-for-authenticator-apps.md index 8203df16042b..a9dc6fb603d1 100644 --- a/aspnetcore/blazor/security/server/qrcodes-for-authenticator-apps.md +++ b/aspnetcore/blazor/security/qrcodes-for-authenticator-apps.md @@ -5,7 +5,7 @@ description: Discover how to enable QR code generation for TOTP authenticator ap ms.author: riande monikerRange: '>= aspnetcore-8.0' ms.date: 11/12/2024 -uid: blazor/security/server/qrcodes-for-authenticator-apps +uid: blazor/security/qrcodes-for-authenticator-apps --- # Enable QR code generation for TOTP authenticator apps in an ASP.NET Core Blazor Web App diff --git a/aspnetcore/blazor/security/server/index.md b/aspnetcore/blazor/security/server/index.md deleted file mode 100644 index 7cff19b1384c..000000000000 --- a/aspnetcore/blazor/security/server/index.md +++ /dev/null @@ -1,440 +0,0 @@ ---- -title: Secure ASP.NET Core server-side Blazor apps -author: guardrex -description: Learn how to secure server-side Blazor apps as ASP.NET Core applications. -monikerRange: '>= aspnetcore-3.1' -ms.author: riande -ms.custom: mvc -ms.date: 11/12/2024 -uid: blazor/security/server/index ---- -# Secure ASP.NET Core server-side Blazor apps - -[!INCLUDE[](~/includes/not-latest-version.md)] - -This article explains how to secure server-side Blazor apps as ASP.NET Core applications. - -Server-side Blazor apps are configured for security in the same manner as ASP.NET Core apps. For more information, see the articles under . - -The authentication context is only established when the app starts, which is when the app first connects to the WebSocket. The authentication context is maintained for the lifetime of the circuit. Apps periodically revalidate the user's authentication state every 30 minutes. - -If the app must capture users for custom services or react to updates to the user, see . - -Blazor differs from a traditional server-rendered web apps that make new HTTP requests with cookies on every page navigation. Authentication is checked during navigation events. However, cookies aren't involved. Cookies are only sent when making an HTTP request to a server, which isn't what happens when the user navigates in a Blazor app. During navigation, the user's authentication state is checked within Blazor's SignalR circuit, which you can update at any time on the server using a revalidating `AuthenticationStateProvider`](#additional-authentication-state-providers). - -> [!IMPORTANT] -> Implementing a custom `NavigationManager` to achieve authentication validation during navigation isn't recommended. If the app must execute custom authentication state logic during navigation, use a [custom `AuthenticationStateProvider`](xref:blazor/security/authentication-state#implement-a-custom-authenticationstateprovider). - -> [!NOTE] -> The code examples in this article adopt [nullable reference types (NRTs) and .NET compiler null-state static analysis](xref:migration/50-to-60#nullable-reference-types-nrts-and-net-compiler-null-state-static-analysis), which are supported in ASP.NET Core in .NET 6 or later. When targeting ASP.NET Core 5.0 or earlier, remove the null type designation (`?`) from the examples in this article. - -## Server-side security of sensitive data and credentials - -In test/staging and production environments, server-side Blazor code and web APIs should use secure authentication flows that avoid maintaining credentials within project code or configuration files. Outside of local development testing, we recommend avoiding the use of environment variables to store sensitive data, as environment variables aren't the most secure approach. For local development testing, the [Secret Manager tool](xref:security/app-secrets) is recommended for securing sensitive data. For more information, see the following resources: - -* [Secure authentication flows (ASP.NET Core documentation)](xref:security/index#secure-authentication-flows) -* [Managed identities for Microsoft Azure services (Blazor documentation)](xref:blazor/security/index#managed-identities-for-microsoft-azure-services) - -For client-side and server-side local development and testing, use the [Secret Manager tool](xref:security/app-secrets) to secure sensitive credentials. - -## Project template - -Create a new server-side Blazor app by following the guidance in . - -# [Visual Studio](#tab/visual-studio) - -After choosing the server-side app template and configuring the project, select the app's authentication under **Authentication type**: - -:::moniker range=">= aspnetcore-8.0" - -* **None** (default): No authentication. -* **Individual Accounts**: User accounts are stored within the app using ASP.NET Core [Identity](xref:security/authentication/identity). - -:::moniker-end - -:::moniker range="< aspnetcore-8.0" - -* **None** (default): No authentication. -* **Individual Accounts**: User accounts are stored within the app using ASP.NET Core [Identity](xref:security/authentication/identity). -* **Microsoft identity platform**: For more information, see . -* **Windows**: Use Windows Authentication. - -:::moniker-end - -# [Visual Studio Code](#tab/visual-studio-code) - -When issuing the .NET CLI command to create and configure the server-side Blazor app, indicate the authentication mechanism with the `-au|--auth` option: - -```dotnetcli --au {AUTHENTICATION} -``` - -> [!NOTE] -> For the full command, see . - -Permissible authentication values for the `{AUTHENTICATION}` placeholder are shown in the following table. - -:::moniker range=">= aspnetcore-8.0" - -| Authentication mechanism | Description | -| ------------------------ | ----------- | -| `None` (default) | No authentication | -| `Individual` | Users stored in the app with ASP.NET Core Identity | - -:::moniker-end - -:::moniker range="< aspnetcore-8.0" - -| Authentication mechanism | Description | -| ------------------------ | ----------- | -| `None` (default) | No authentication | -| `Individual` | Users stored in the app with ASP.NET Core Identity | -| `IndividualB2C` | Users stored in [Azure AD B2C](xref:security/authentication/azure-ad-b2c) | -| `SingleOrg` | Organizational authentication for a single tenant | -| `MultiOrg` | Organizational authentication for multiple tenants | -| `Windows` | Windows Authentication | - -:::moniker-end - -For more information, see the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide. - -# [.NET CLI](#tab/net-cli/) - -When issuing the .NET CLI command to create and configure the server-side Blazor app, indicate the authentication mechanism with the `-au|--auth` option: - -```dotnetcli --au {AUTHENTICATION} -``` - -> [!NOTE] -> For the full command, see . - -Permissible authentication values for the `{AUTHENTICATION}` placeholder are shown in the following table. - -:::moniker range=">= aspnetcore-8.0" - -| Authentication mechanism | Description | -| ------------------------ | ----------- | -| `None` (default) | No authentication | -| `Individual` | Users stored in the app with ASP.NET Core Identity | - -:::moniker-end - -:::moniker range="< aspnetcore-8.0" - -| Authentication mechanism | Description | -| ------------------------ | ----------- | -| `None` (default) | No authentication | -| `Individual` | Users stored in the app with ASP.NET Core Identity | -| `IndividualB2C` | Users stored in [Azure AD B2C](xref:security/authentication/azure-ad-b2c) | -| `SingleOrg` | Organizational authentication for a single tenant | -| `MultiOrg` | Organizational authentication for multiple tenants | -| `Windows` | Windows Authentication | - -:::moniker-end - -For more information: - -* See the [`dotnet new`](/dotnet/core/tools/dotnet-new) command in the .NET Core Guide. -* Execute the help command for the template in a command shell: - - ```dotnetcli - dotnet new {PROJECT TEMPLATE} --help - ``` - - In the preceding command, the `{PROJECT TEMPLATE}` placeholder is the project template. - ---- - -:::moniker range=">= aspnetcore-8.0" - -## Blazor Identity UI (Individual Accounts) - -Blazor supports generating a full Blazor-based Identity UI when you choose the authentication option for *Individual Accounts*. - -The Blazor Web App template scaffolds Identity code for a SQL Server database. The command line version uses SQLite and includes a SQLite database for Identity. - -The template: - -* Supports interactive server-side rendering (interactive SSR) and client-side rendering (CSR) scenarios with authenticated users. -* Adds Identity Razor components and related logic for routine authentication tasks, such as signing users in and out. The Identity components also support advanced Identity features, such as [account confirmation and password recovery](xref:security/authentication/accconfirm) and [multifactor authentication](xref:security/authentication/mfa) using a third-party app. Note that the Identity components themselves don't support interactivity. -* Adds the Identity-related packages and dependencies. -* References the Identity packages in `_Imports.razor`. -* Creates a custom user Identity class (`ApplicationUser`). -* Creates and registers an EF Core database context (`ApplicationDbContext`). -* Configures routing for the built-in Identity endpoints. -* Includes Identity validation and business logic. - -To inspect the Blazor framework's Identity components, access them in the `Pages` and `Shared` folders of the [`Account` folder in the Blazor Web App project template (reference source)](https://github.com/dotnet/aspnetcore/tree/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account). - -:::moniker-end - -:::moniker range=">= aspnetcore-9.0" - -When you choose the Interactive WebAssembly or Interactive Auto render modes, the server handles all authentication and authorization requests, and the Identity components render statically on the server in the Blazor Web App's main project. - -The framework provides a custom in both the server and client (`.Client`) projects to flow the user's authentication state to the browser. The server project calls , while the client project calls . Authenticating on the server rather than the client allows the app to access authentication state during prerendering and before the .NET WebAssembly runtime is initialized. The custom implementations use the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) () to serialize the authentication state into HTML comments and then read it back from WebAssembly to create a new instance. For more information, see the [Manage authentication state in Blazor Web Apps](#manage-authentication-state-in-blazor-web-apps) section. - -Only for Interactive Server solutions, [`IdentityRevalidatingAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs) is a server-side that revalidates the security stamp for the connected user every 30 minutes an interactive circuit is connected. - -:::moniker-end - -:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" - -When you choose the Interactive WebAssembly or Interactive Auto render modes, the server handles all authentication and authorization requests, and the Identity components render statically on the server in the Blazor Web App's main project. The project template includes a [`PersistentAuthenticationStateProvider` class (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs) in the `.Client` project to synchronize the user's authentication state between the server and the browser. The class is a custom implementation of . The provider uses the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) () to prerender the authentication state and persist it to the page. - -In the main project of a Blazor Web App, the authentication state provider is named either [`IdentityRevalidatingAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs) (Server interactivity solutions only) or [`PersistingRevalidatingAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs) (WebAssembly or Auto interactivity solutions). - -:::moniker-end - -:::moniker range=">= aspnetcore-8.0" - -Blazor Identity depends on instances not [created by a factory](xref:blazor/blazor-ef-core#new-dbcontext-instances), which is intentional because is sufficient for the project template's Identity components to render statically without supporting interactivity. - -For a description on how global interactive render modes are applied to non-Identity components while at the same time enforcing static SSR for the Identity components, see . - -For more information on persisting prerendered state, see . - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -## Manage authentication state in Blazor Web Apps - -*This section applies to Blazor Web Apps that adopt:* - -* Individual Accounts -* *Client-side rendering (CSR, WebAssembly-based interactivity).* - -A client-side authentication state provider is only used within Blazor and isn't integrated with the ASP.NET Core authentication system. During prerendering, Blazor respects the metadata defined on the page and uses the ASP.NET Core authentication system to determine if the user is authenticated. When a user navigates from one page to another, a client-side authentication provider is used. When the user refreshes the page (full-page reload), the client-side authentication state provider isn't involved in the authentication decision on the server. Since the user's state isn't persisted by the server, any authentication state maintained client-side is lost. - -To address this, the best approach is to perform authentication within the ASP.NET Core authentication system. The client-side authentication state provider only takes care of reflecting the user's authentication state. Examples for how to accomplish this with authentication state providers are demonstrated by the Blazor Web App project template and described below. - -:::moniker-end - -:::moniker range=">= aspnetcore-9.0" - -In the server project's `Program` file, call , which serializes the returned by the server-side using the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) (): - -```csharp -builder.Services.AddRazorComponents() - .AddInteractiveWebAssemblyComponents() - .AddAuthenticationStateSerialization(); -``` - -The API only serializes the server-side name and role claims for access in the browser. To include all claims, set `SerializeAllClaims` to `true` in the server-side call to : - -```csharp -builder.Services.AddRazorComponents() - .AddInteractiveWebAssemblyComponents() - .AddAuthenticationStateSerialization( - options => options.SerializeAllClaims = true); -``` - -In the client (`.Client`) project's `Program` file, call , which adds an where the is deserialized from the server using `AuthenticationStateData` and the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) (). There should be a corresponding call to in the server project. - -```csharp -builder.Services.AddAuthorizationCore(); -builder.Services.AddCascadingAuthenticationState(); -builder.Services.AddAuthenticationStateDeserialization(); -``` - -:::moniker-end - -:::moniker range=">= aspnetcore-8.0 < aspnetcore-9.0" - -* [`PersistingRevalidatingAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs): For Blazor Web Apps that adopt interactive server-side rendering (interactive SSR) and client-side rendering (CSR). This is a server-side that revalidates the security stamp for the connected user every 30 minutes an interactive circuit is connected. It also uses the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) to flow the authentication state to the client, which is then fixed for the lifetime of CSR. - -* [`PersistingServerAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/PersistingServerAuthenticationStateProvider.cs): For Blazor Web Apps that only adopt CSR. This is a server-side that uses the [Persistent Component State service](xref:blazor/components/prerender#persist-prerendered-state) to flow the authentication state to the client, which is then fixed for the lifetime of CSR. - -* [`PersistentAuthenticationStateProvider` (reference source)](https://github.com/dotnet/aspnetcore/blob/release/8.0/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs): For Blazor Web Apps that adopt CSR. This is a client-side that determines the user's authentication state by looking for data persisted in the page when it was rendered on the server. This authentication state is fixed for the lifetime of CSR. If the user needs to log in or out, a full-page reload is required. This only provides a user name and email for display purposes. It doesn't include tokens that authenticate to the server when making subsequent requests, which is handled separately using a cookie that's included on `HttpClient` requests to the server. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -:::moniker-end - -:::moniker range="< aspnetcore-8.0" - -## Scaffold Identity - -:::moniker-end - -:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0" - -For more information on scaffolding Identity into a server-side Blazor app, see . - -:::moniker-end - -:::moniker range="< aspnetcore-6.0" - -Scaffold Identity into a server-side Blazor app: - -* [Without existing authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-server-side-blazor-app-without-existing-authorization). -* [With authorization](xref:security/authentication/scaffold-identity#scaffold-identity-into-a-server-side-blazor-app-with-authorization). - -:::moniker-end - -## Additional claims and tokens from external providers - -To store additional claims from external providers, see . - -## Azure App Service on Linux with Identity Server - -Specify the issuer explicitly when deploying to Azure App Service on Linux with Identity Server. For more information, see . - -## Inject `AuthenticationStateProvider` for services scoped to a component - -Don't attempt to resolve within a service that's scoped to a component by [`OwningComponentBase`](xref:fundamentals/dependency-injection#owningcomponentbase) because it results in the creation of a new instance of the that isn't correctly initialized. - -To access the within a service scoped to a component, inject the directly into the component and pass the service instance to the scoped service as a parameter. This approach ensures that the correct, initialized instance of the is used for each user app instance. - -`ExampleService.cs`: - -```csharp -public class ExampleService -{ - public async Task ExampleMethod(AuthenticationStateProvider authStateProvider) - { - var authState = await authStateProvider.GetAuthenticationStateAsync(); - var user = authState.User; - - if (user.Identity is not null && user.Identity.IsAuthenticated) - { - return $"{user.Identity.Name} is authenticated."; - } - else - { - return "The user is NOT authenticated."; - } - } -} -``` - -In the `Program` file, the `ExampleService` is registered as a scoped service. In this example, the service is scoped to a component with . For general guidance on the lifetime of scoped services, see . - -```csharp -.AddScoped(); -``` - -In the following `InjectAuthStateProvider` component: - -* The component inherits . -* The is injected and passed to `ExampleService.ExampleMethod`. -* `ExampleService` is resolved with and , which returns the correct, initialized instance of `ExampleService` that exists for the lifetime of the user's circuit. - -`InjectAuthStateProvider.razor`: - -```razor -@page "/inject-auth-state-provider" -@inherits OwningComponentBase -@inject AuthenticationStateProvider AuthenticationStateProvider - -

Inject AuthenticationStateProvider Example

- -

@message

- -@code { - private string? message; - private ExampleService? ExampleService { get; set; } - - protected override async Task OnInitializedAsync() - { - ExampleService = ScopedServices.GetRequiredService(); - - message = await ExampleService.ExampleMethod(AuthenticationStateProvider); - } -} -``` - -For more information, see the guidance on in . - -## Unauthorized content display while prerendering with a custom `AuthenticationStateProvider` - -To avoid showing unauthorized content, for example content in an [`AuthorizeView` component](xref:blazor/security/index#authorizeview-component), while prerendering with a [custom `AuthenticationStateProvider`](xref:blazor/security/authentication-state#implement-a-custom-authenticationstateprovider), adopt ***one*** of the following approaches: - -* Implement for the custom to support prerendering: For an example implementation of , see the Blazor framework's implementation in [`ServerAuthenticationStateProvider.cs` (reference source)](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Endpoints/src/DependencyInjection/ServerAuthenticationStateProvider.cs). - - [!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -:::moniker range=">= aspnetcore-8.0" - -* Disable prerendering: Indicate the render mode with the `prerender` parameter set to `false` at the highest-level component in the app's component hierarchy that isn't a root component. - - > [!NOTE] - > Making a root component interactive, such as the `App` component, isn't supported. Therefore, prerendering can't be disabled directly by the `App` component. - - For apps based on the Blazor Web App project template, prerendering is typically disabled where the `Routes` component is used in the `App` component (`Components/App.razor`) : - - ```razor - - ``` - - Also, disable prerendering for the `HeadOutlet` component: - - ```razor - - ``` - - You can also selectively control the render mode applied to the `Routes` component instance. For example, see . - -:::moniker-end - -:::moniker range="< aspnetcore-8.0" - -* Disable prerendering: Open the `_Host.cshtml` file and change the `render-mode` attribute of the [Component Tag Helper](xref:mvc/views/tag-helpers/builtin-th/component-tag-helper) to : - - ```cshtml - - ``` - -:::moniker-end - -* Authenticate the user on the server before the app starts: To adopt this approach, the app must respond to a user's initial request with the Identity-based sign-in page or view and prevent any requests to Blazor endpoints until they're authenticated. For more information, see . After authentication, unauthorized content in prerendered Razor components is only shown when the user is truly unauthorized to view the content. - -## User state management - -In spite of the word "state" in the name, isn't for storing *general user state*. only indicates the user's authentication state to the app, whether they are signed into the app and who they are signed in as. - -Authentication uses the same ASP.NET Core Identity authentication as Razor Pages and MVC apps. The user state stored for ASP.NET Core Identity flows to Blazor without adding additional code to the app. Follow the guidance in the ASP.NET Core Identity articles and tutorials for the Identity features to take effect in the Blazor parts of the app. - -For guidance on general state management outside of ASP.NET Core Identity, see . - -## Additional authentication state providers - -Two additional classes derived from help with managing authentication state on the server: - -* ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Endpoints/src/DependencyInjection/ServerAuthenticationStateProvider.cs)): A default used by the Blazor framework to manage authentication state on the server when a more specific provider isn't registered. - -* ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/Circuits/RevalidatingServerAuthenticationStateProvider.cs)): A base class for services that receive an authentication state from the host environment and revalidate it at regular intervals. See the [Blazor Web App project template](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs) for an example implementation. Override to change the default 30 minute revalidation interval. - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -## Authentication state management at sign out - -Server-side Blazor persists user authentication state for the lifetime of the circuit, including across browser tabs. To proactively sign off a user across browser tabs when the user signs out on one tab, you must implement a ([reference source](https://github.com/dotnet/aspnetcore/blob/main/src/Components/Server/src/Circuits/RevalidatingServerAuthenticationStateProvider.cs)) with a short . - -[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] - -:::moniker range=">= aspnetcore-8.0" - -## Temporary redirection URL validity duration - -*This section applies to Blazor Web Apps.* - -Use the option to get or set the lifetime of ASP.NET Core Data Protection validity for temporary redirection URLs emitted by Blazor server-side rendering. These are only used transiently, so the lifetime only needs to be long enough for a client to receive the URL and begin navigation to it. However, it should also be long enough to allow for clock skew across servers. The default value is five minutes. - -In the following example the value is extended to seven minutes: - -```csharp -builder.Services.AddRazorComponents(options => - options.TemporaryRedirectionUrlValidityDuration = - TimeSpan.FromMinutes(7)); -``` - -:::moniker-end - -## Additional resources - -* [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/entra/identity-platform/quickstart-v2-aspnet-core-webapp) -* [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/entra/identity-platform/quickstart-v2-aspnet-core-web-api) -* : 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. diff --git a/aspnetcore/blazor/security/server/static-server-side-rendering.md b/aspnetcore/blazor/security/static-server-side-rendering.md similarity index 98% rename from aspnetcore/blazor/security/server/static-server-side-rendering.md rename to aspnetcore/blazor/security/static-server-side-rendering.md index bf4d33f7b914..85e030a6a624 100644 --- a/aspnetcore/blazor/security/server/static-server-side-rendering.md +++ b/aspnetcore/blazor/security/static-server-side-rendering.md @@ -6,7 +6,7 @@ monikerRange: '>= aspnetcore-8.0' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 -uid: blazor/security/server/static-server-side-rendering +uid: blazor/security/static-server-side-rendering --- # Threat mitigation guidance for ASP.NET Core Blazor static server-side rendering @@ -22,7 +22,7 @@ All of the general security considerations defined for the interactive rendering The server-side rendering (SSR) model is based on the traditional request/response model of HTTP. As such, there are common areas of concern between SSR and request/response HTTP. General security considerations and specific threats must be successfully mitigated. The framework provides built-in mechanisms for managing some of these threats, but other threats are specific to app code and must be handled by the app. These threats can be categorized as follows: -* Authentication and authorization: The app must ensure that the user is authenticated and authorized to access the app and the resources it exposes. The framework provides built-in mechanisms for authentication and authorization, but the app must ensure that the mechanisms are properly configured and used. The built-in mechanisms for authentication and authorization are covered in the [Blazor documentation's *Server* security node](xref:blazor/security/server/index) and in the [ASP.NET Core documentation's *Security and Identity* node](xref:security/index), so they won't be covered here. +* Authentication and authorization: The app must ensure that the user is authenticated and authorized to access the app and the resources it exposes. The framework provides built-in mechanisms for authentication and authorization, but the app must ensure that the mechanisms are properly configured and used. The built-in mechanisms for authentication and authorization are covered in the [Blazor documentation's *Server* security node](xref:blazor/security/index) and in the [ASP.NET Core documentation's *Security and Identity* node](xref:security/index), so they won't be covered here. * Input validation and sanitization: All input arriving from a client must be validated and sanitized before use. Otherwise, the app might be exposed to attacks, such as SQL injection, cross-site scripting, cross-site request forgery, open redirection, and other forms of attacks. The input might come from anywhere in the request. @@ -51,7 +51,7 @@ The framework provides the following mechanisms to help with input validation an * All bound form data is validated for basic correctness. If an input can't be parsed, the binding process reports an error that the app can discover before taking any action with the data. The built-in component takes this into account before invoking the form callback. Blazor avoids executing the callback if there are one or more binding errors. * The framework uses an antiforgery token to protect against cross-site request forgery attacks. For more information, see and . -All input and permissions must be validated on the server at the time of performing a given action to ensure that the data is valid and accurate at that time and that the user is allowed to perform the action. This approach is consistent with the [security guidance provided for interactive server-side rendering](xref:blazor/security/server/interactive-server-side-rendering). +All input and permissions must be validated on the server at the time of performing a given action to ensure that the data is valid and accurate at that time and that the user is allowed to perform the action. This approach is consistent with the [security guidance provided for interactive server-side rendering](xref:blazor/security/interactive-server-side-rendering). ## Session management diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery.md b/aspnetcore/blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery.md new file mode 100644 index 000000000000..729507bb94ae --- /dev/null +++ b/aspnetcore/blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery.md @@ -0,0 +1,627 @@ +--- +title: Account confirmation and password recovery in ASP.NET Core Blazor WebAssembly with ASP.NET Core Identity +author: guardrex +description: Learn how to configure an ASP.NET Core Blazor WebAssembly app with ASP.NET Core Identity with email confirmation and password recovery. +ms.author: riande +monikerRange: '>= aspnetcore-8.0' +ms.date: 10/31/2024 +uid: blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery +--- +# Account confirmation and password recovery in ASP.NET Core Blazor WebAssembly with ASP.NET Core Identity + +This article explains how to configure an ASP.NET Core Blazor WebAssembly app with ASP.NET Core Identity with email confirmation and password recovery. + +> [!NOTE] +> This article only applies standalone Blazor WebAssembly apps with ASP.NET Core Identity. To implement email confirmation and password recovery for Blazor Web Apps, see . + +## Namespace + +The namespaces used by the examples in this article are: + +* `Backend` for the backend server web API project ("server project" in this article). +* `BlazorWasmAuth` for the front-end client standalone Blazor WebAssembly app ("client project" in this article). + +These namespaces correspond to the projects in the `BlazorWebAssemblyStandaloneWithIdentity` sample solution in the [`dotnet/blazor-samples` GitHub repository](https://github.com/dotnet/blazor-samples). For more information, see . + +If you aren't using the `BlazorWebAssemblyStandaloneWithIdentity` sample solution, change the namespaces in the code examples to use the namespaces of your projects. + +## Select and configure an email provider for the server project + +In this article, [Mailchimp's Transactional API](https://mailchimp.com/developer/transactional/api/) is used via [Mandrill.net](https://www.nuget.org/packages/Mandrill.net) to send email. We recommend using an email service to send email rather than SMTP. SMTP is difficult to configure and secure properly. Whichever email service you use, access their guidance for .NET apps, create an account, configure an API key for their service, and install any NuGet packages required. + +In the server project, create a class to hold the secret email provider API key. The example in this article uses a class named `AuthMessageSenderOptions` with an `EmailAuthKey` property to hold the key. + +`AuthMessageSenderOptions.cs`: + +```csharp +namespace Backend; + +public class AuthMessageSenderOptions +{ + public string? EmailAuthKey { get; set; } +} +``` + +Register the `AuthMessageSenderOptions` configuration instance in the server project's `Program` file: + +```csharp +builder.Services.Configure(builder.Configuration); +``` + +## Configure a user secret for the provider's security key + +If the server project has already been initialized for the [Secret Manager tool](xref:security/app-secrets), it will already have a app secrets identifier (``) in its project file (`.csproj`). In Visual Studio, you can tell if the app secrets ID is present by looking at the **Properties** panel when the project is selected in **Solution Explorer**. If the app hasn't been initialized, execute the following command in a command shell opened to the server project's directory. In Visual Studio, you can use the Developer PowerShell command prompt (use the `cd` command to change the directory to the server project after you open the command shell). + +```dotnetcli +dotnet user-secrets init +``` + +Set the API key with the Secret Manager tool. In the following example, the key name is `EmailAuthKey` to match `AuthMessageSenderOptions.EmailAuthKey`, and the key is represented by the `{KEY}` placeholder. Execute the following command with the API key: + +```dotnetcli +dotnet user-secrets set "EmailAuthKey" "{KEY}" +``` + +If using Visual Studio, you can confirm the secret is set by right-clicking the server project in **Solution Explorer** and selecting **Manage User Secrets**. + +For more information, see . + +[!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)] + +## Implement `IEmailSender` in the server project + +The following example is based on Mailchimp's Transactional API using [Mandrill.net](https://www.nuget.org/packages/Mandrill.net). For a different provider, refer to their documentation on how to implement sending an email message. + +Add the [Mandrill.net](https://www.nuget.org/packages/Mandrill.net) NuGet package to the server project. + +Add the following `EmailSender` class to implement . In the following example, `AppUser` is an . The message HTML markup can be further customized. As long as the `message` passed to `MandrillMessage` starts with the `<` character, the Mandrill.net API assumes that the message body is composed in HTML. + +`EmailSender.cs`: + +```csharp +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using Mandrill; +using Mandrill.Model; + +namespace Backend; + +public class EmailSender(IOptions optionsAccessor, + ILogger logger) : IEmailSender +{ + private readonly ILogger logger = logger; + + public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value; + + public Task SendConfirmationLinkAsync(AppUser user, string email, + string confirmationLink) => SendEmailAsync(email, "Confirm your email", + "Please confirm your account by " + + $"clicking here."); + + public Task SendPasswordResetLinkAsync(AppUser user, string email, + string resetLink) => SendEmailAsync(email, "Reset your password", + "Please reset your password by " + + $"clicking here."); + + public Task SendPasswordResetCodeAsync(AppUser user, string email, + string resetCode) => SendEmailAsync(email, "Reset your password", + "Please reset your password " + + $"using the following code:
{resetCode}"); + + public async Task SendEmailAsync(string toEmail, string subject, string message) + { + if (string.IsNullOrEmpty(Options.EmailAuthKey)) + { + throw new Exception("Null EmailAuthKey"); + } + + await Execute(Options.EmailAuthKey, subject, message, toEmail); + } + + public async Task Execute(string apiKey, string subject, string message, + string toEmail) + { + var api = new MandrillApi(apiKey); + var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail, + subject, message); + await api.Messages.SendAsync(mandrillMessage); + + logger.LogInformation("Email to {EmailAddress} sent!", toEmail); + } +} +``` + +> [!NOTE] +> Body content for messages might require special encoding for the email service provider. If links in the message body can't be followed in the email message, consult the service provider's documentation to troubleshoot the problem. + +Add the following service registration to the server project's `Program` file: + +```csharp +builder.Services.AddTransient, EmailSender>(); +``` + +## Configure the server project to require email confirmation + +In the server project's `Program` file, require a confirmed email address to sign in to the app. + +Locate the line that calls and set the property to `true`: + +```diff +- builder.Services.AddIdentityCore() ++ builder.Services.AddIdentityCore(o => o.SignIn.RequireConfirmedEmail = true) +``` + +## Update the client project's account registration response + +In the client project's `Register` component (`Components/Identity/Register.razor`), change the message to users on a successful account registration to instruct them to confirm their account. The following example includes a link to trigger Identity on the server to resend the confirmation email. + +```diff +-
+- You successfully registered. Now you can login. +-
++
++ You successfully registered. You must now confirm your account by clicking ++ the link in the email that was sent to you. After confirming your account, ++ you can login to the app. ++ Resend confirmation email ++
+``` + +## Update seed data code to confirm seeded accounts + +In the server project's seed data class (`SeedData.cs`), change the code in the `InitializeAsync` method to confirm the seeded accounts, which avoids requiring email address confirmation for each test run of the solution with one of the accounts: + +```diff +- if (appUser is not null && user.RoleList is not null) +- { +- await userManager.AddToRolesAsync(appUser, user.RoleList); +- } ++ if (appUser is not null) ++ { ++ if (user.RoleList is not null) ++ { ++ await userManager.AddToRolesAsync(appUser, user.RoleList); ++ } ++ ++ var token = await userManager.GenerateEmailConfirmationTokenAsync(appUser); ++ await userManager.ConfirmEmailAsync(appUser, token); ++ } +``` + +## Enable account confirmation after a site has users + +Enabling account confirmation on a site with users locks out all the existing users. Existing users are locked out because their accounts aren't confirmed. Use one of the following approaches, which are beyond the scope of this article: + +* Update the database to mark all existing users as confirmed. +* Batch-send emails with confirmation links to all existing users, which requires each user to confirm their own account. + +## Password recovery + +Password recovery requires the server app to adopt an email provider in order to send password reset codes to users. Therefore, follow the guidance earlier in this article to adopt an email provider: + +* [Select and configure an email provider for the server project](#select-and-configure-an-email-provider-for-the-server-project) +* [Configure a user secret for the provider's security key](#configure-a-user-secret-for-the-providers-security-key) +* [Implement `IEmailSender` in the server project](#implement-iemailsender-in-the-server-project) + +Password recovery is a two-step process: + +1. A POST request is made to the `/forgotPassword` endpoint provided by in the server project. A message in the UI instructs the user to check their email for a reset code. +1. A POST request is made to the `/resetPassword` endpoint of the server project with the user's email address, password reset code, and new password. + +The preceding steps are demonstrated by the following implementation guidance for the [sample solution](xref:blazor/security/webassembly/standalone-with-identity/index#sample-apps). + +In the client project, add the following method signatures to the `IAccountManagement` class (`Identity/IAccountManagement.cs`): + +```csharp +public Task ForgotPasswordAsync(string email); + +public Task ResetPasswordAsync(string email, string resetCode, + string newPassword); +``` + +In the client project, add implementations for the preceding methods in the `CookieAuthenticationStateProvider` class (`Identity/CookieAuthenticationStateProvider.cs`): + +```csharp +/// +/// Begin the password recovery process by issuing a POST request to the +/// '/forgotPassword' endpoint. +/// +/// The user's email address. +/// A indicating success or failure. +public async Task ForgotPasswordAsync(string email) +{ + try + { + // make the request + var result = await httpClient.PostAsJsonAsync( + "forgotPassword", new + { + email + }); + + // successful? + if (result.IsSuccessStatusCode) + { + return true; + } + } + catch { } + + // unknown error + return false; +} + +/// +/// Reset the user's password by issuing a POST request to the +/// '/resetPassword' endpoint. +/// +/// The user's email address. +/// The user's reset code. +/// The user's new password. +/// The result serialized to a . +/// +public async Task ResetPasswordAsync(string email, string resetCode, + string newPassword) +{ + string[] defaultDetail = ["An unknown error prevented resetting the password."]; + + try + { + // make the request + var result = await httpClient.PostAsJsonAsync( + "resetPassword", new + { + email, + resetCode, + newPassword + }); + + // successful? + if (result.IsSuccessStatusCode) + { + return new FormResult { Succeeded = true }; + } + + // body should contain details about why it failed + var details = await result.Content.ReadAsStringAsync(); + var problemDetails = JsonDocument.Parse(details); + var errors = new List(); + var errorList = problemDetails.RootElement.GetProperty("errors"); + + foreach (var errorEntry in errorList.EnumerateObject()) + { + if (errorEntry.Value.ValueKind == JsonValueKind.String) + { + errors.Add(errorEntry.Value.GetString()!); + } + else if (errorEntry.Value.ValueKind == JsonValueKind.Array) + { + errors.AddRange( + errorEntry.Value.EnumerateArray().Select( + e => e.GetString() ?? string.Empty) + .Where(e => !string.IsNullOrEmpty(e))); + } + } + + // return the error list + return new FormResult + { + Succeeded = false, + ErrorList = problemDetails == null ? defaultDetail : [.. errors] + }; + } + catch { } + + // unknown error + return new FormResult + { + Succeeded = false, + ErrorList = defaultDetail + }; +} +``` + +In the client project, add the following `ForgotPassword` component. + +> [!NOTE] +> Code lines in the following example are broken across two or more lines to eliminate or reduce horizontal scrolling in this article, but you can place the following code as shown into a test app. The code executes regardless of the artificial line breaks. + +`Components/Identity/ForgotPassword.razor`: + +```razor +@page "/forgot-password" +@using System.ComponentModel.DataAnnotations +@using BlazorWasmAuth.Identity +@inject IAccountManagement Acct + +Forgot your password? + +

Forgot your password?

+

Provide your email address and select the Reset password button.

+
+
+
+ @if (!passwordResetCodeSent) + { + + + + +
+ + + +
+ +
+ } + else + { + if (passwordResetSuccess) + { + if (errors) + { + foreach (var error in errorList) + { +
@error
+ } + } + else + { +
+ Your password was reset. You may login + to the app with your new password. +
+ } + } + else + { +
+ A password reset code has been sent to your email address. + Obtain the code from the email for this form. +
+ + + + +
+ + + +
+
+ + + +
+
+ + + +
+ +
+ } + } +
+
+ +@code { + private bool passwordResetCodeSent; + private bool passwordResetSuccess, errors; + private string[] errorList = []; + + [SupplyParameterFromForm(FormName = "forgot-password")] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromForm(FormName = "reset-password")] + private ResetModel Reset { get; set; } = new(); + + private async Task OnValidSubmitStep1Async() + { + passwordResetCodeSent = await Acct.ForgotPasswordAsync(Input.Email); + } + + private async Task OnValidSubmitStep2Async() + { + var result = await Acct.ResetPasswordAsync(Input.Email, Reset.ResetCode, + Reset.NewPassword); + + if (result.Succeeded) + { + passwordResetSuccess = true; + + } + else + { + errors = true; + errorList = result.ErrorList; + } + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } = string.Empty; + } + + private sealed class ResetModel + { + [Required] + [Base64String] + public string ResetCode { get; set; } = string.Empty; + + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at " + + "max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string NewPassword { get; set; } = string.Empty; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation " + + "password do not match.")] + public string ConfirmPassword { get; set; } = string.Empty; + } +} +``` + +In the `Login` component (`Components/Identity/Login.razor`) of the client project immediately before the closing `` tag, add a forgot password link to reach the `ForgotPassword` component: + +```html + +``` + +## Email and activity timeout + +The default inactivity timeout is 14 days. In the server project, the following code sets the inactivity timeout to five days with sliding expiration: + +```csharp +builder.Services.ConfigureApplicationCookie(options => { + options.ExpireTimeSpan = TimeSpan.FromDays(5); + options.SlidingExpiration = true; +}); +``` + +## Change all ASP.NET Core Data Protection token lifespans + +In the server project, the following code changes Data Protection tokens' timeout period to three hours: + +```csharp +builder.Services.Configure(options => + options.TokenLifespan = TimeSpan.FromHours(3)); +``` + +The built-in Identity user tokens ([AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Extensions.Core/src/TokenOptions.cs)) have a [one day timeout](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Core/src/DataProtectionTokenProviderOptions.cs). + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +## Change the email token lifespan + +The default token lifespan of the [Identity user tokens](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Extensions.Core/src/TokenOptions.cs) is [one day](https://github.com/dotnet/AspNetCore/blob/main/src/Identity/Core/src/DataProtectionTokenProviderOptions.cs). + +[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)] + +To change the email token lifespan, add a custom and to the server project. + +`CustomTokenProvider.cs`: + +```csharp +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; + +namespace BlazorSample; + +public class CustomEmailConfirmationTokenProvider + : DataProtectorTokenProvider where TUser : class +{ + public CustomEmailConfirmationTokenProvider( + IDataProtectionProvider dataProtectionProvider, + IOptions options, + ILogger> logger) + : base(dataProtectionProvider, options, logger) + { + } +} + +public class EmailConfirmationTokenProviderOptions + : DataProtectionTokenProviderOptions +{ + public EmailConfirmationTokenProviderOptions() + { + Name = "EmailDataProtectorTokenProvider"; + TokenLifespan = TimeSpan.FromHours(4); + } +} + +public class CustomPasswordResetTokenProvider + : DataProtectorTokenProvider where TUser : class +{ + public CustomPasswordResetTokenProvider( + IDataProtectionProvider dataProtectionProvider, + IOptions options, + ILogger> logger) + : base(dataProtectionProvider, options, logger) + { + } +} + +public class PasswordResetTokenProviderOptions : + DataProtectionTokenProviderOptions +{ + public PasswordResetTokenProviderOptions() + { + Name = "PasswordResetDataProtectorTokenProvider"; + TokenLifespan = TimeSpan.FromHours(3); + } +} +``` + +Configure the services to use the custom token provider in the server project's `Program` file: + +```csharp +builder.Services.AddIdentityCore(options => + { + options.SignIn.RequireConfirmedAccount = true; + options.Tokens.ProviderMap.Add("CustomEmailConfirmation", + new TokenProviderDescriptor( + typeof(CustomEmailConfirmationTokenProvider))); + options.Tokens.EmailConfirmationTokenProvider = + "CustomEmailConfirmation"; + }) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + +builder.Services + .AddTransient>(); +``` + +## Troubleshoot + +If you can't get email working: + +* Set a breakpoint in `EmailSender.Execute` to verify `SendEmailAsync` is called. +* Create a console app to send email using code similar to `EmailSender.Execute` to debug the problem. +* Review the account email history pages at the email provider's website. +* Check your spam folder for messages. +* Try another email alias on a different email provider, such as Microsoft, Yahoo, or Gmail. +* Try sending to different email accounts. + +## Additional resources + +* [Mandrill.net (GitHub repository)](https://github.com/feinoujc/Mandrill.net) +* [Mailchimp developer: Transactional API](https://mailchimp.com/developer/transactional/docs/fundamentals/) diff --git a/aspnetcore/blazor/security/webassembly/standalone-with-identity.md b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md similarity index 97% rename from aspnetcore/blazor/security/webassembly/standalone-with-identity.md rename to aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md index bff4e36f56f2..d65f44c4a47d 100644 --- a/aspnetcore/blazor/security/webassembly/standalone-with-identity.md +++ b/aspnetcore/blazor/security/webassembly/standalone-with-identity/index.md @@ -6,7 +6,7 @@ monikerRange: '>= aspnetcore-8.0' ms.author: riande ms.custom: mvc ms.date: 11/12/2024 -uid: blazor/security/webassembly/standalone-with-identity +uid: blazor/security/webassembly/standalone-with-identity/index --- # Secure ASP.NET Core Blazor WebAssembly with ASP.NET Core Identity @@ -82,12 +82,15 @@ At this point, you must provide custom code to parse the : +Scenarios covered by the Blazor documentation set: + +* [Account confirmation, password management, and recovery codes](xref:blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery) +* Two-factor authentication (2FA): *Implementation guidance coming soon!* This work is tracked by [Add 2FA/TOTP coverage to the Standalone+Identity article+sample (`dotnet/AspNetCore.Docs` #33772)](https://github.com/dotnet/AspNetCore.Docs/issues/33772). In the meantime, general information to aid you with a custom implementation is available in . + +For information on additional Identity scenarios provided by the API, see : * Secure selected endpoints -* Token authentication -* Two-factor authentication (2FA) -* Recovery codes +* Two-factor authentication (2FA) and recovery codes * User info management ## Use secure authentication flows to maintain sensitive data and credentials diff --git a/aspnetcore/blazor/tutorials/movie-database-app/index.md b/aspnetcore/blazor/tutorials/movie-database-app/index.md index 53fbf2e61fdf..490a5d58bb0c 100644 --- a/aspnetcore/blazor/tutorials/movie-database-app/index.md +++ b/aspnetcore/blazor/tutorials/movie-database-app/index.md @@ -32,7 +32,7 @@ At the end of the tutorial, you'll have a Blazor Web App that can display and ma This tutorial uses a local database that doesn't require user authentication. Production apps should use the most secure authentication flow available. For more information on authentication for deployed test and production Blazor Web Apps, see the following resources: * -* and the following articles in the *Server* security node +* and the following articles in the *Server* security node * * diff --git a/aspnetcore/blazor/tutorials/movie-database-app/part-4.md b/aspnetcore/blazor/tutorials/movie-database-app/part-4.md index 5d63933aabed..4c7d62c009a1 100644 --- a/aspnetcore/blazor/tutorials/movie-database-app/part-4.md +++ b/aspnetcore/blazor/tutorials/movie-database-app/part-4.md @@ -22,7 +22,7 @@ This part of the tutorial series focuses on the database context and directly wo This tutorial uses a local database that doesn't require user authentication. Production apps should use the most secure authentication flow available. For more information on authentication for deployed test and production Blazor Web Apps, see the following resources: * -* and the following articles in the *Server* security node +* and the following articles in the *Server* security node * * @@ -361,7 +361,7 @@ If the app is running, shut the app down by closing the browser's window and pre * [SQLite ALTER TABLE statement (SQLite documentation)](https://sqlite.org/lang_altertable.html) * Blazor Web App security * - * and the following articles in the *Server* security node + * and the following articles in the *Server* security node * * diff --git a/aspnetcore/migration/70-80.md b/aspnetcore/migration/70-80.md index 22f5d40b63e5..15148e4587b1 100644 --- a/aspnetcore/migration/70-80.md +++ b/aspnetcore/migration/70-80.md @@ -480,7 +480,7 @@ Updated configuration guidance appears in the following locations: * [Render Razor components from JavaScript](xref:blazor/components/js-spa-frameworks?view=aspnetcore-8.0&preserve-view=true#render-razor-components-from-javascript): Covers dynamic component registration with . * [Blazor custom elements: Blazor Web App registration](xref:blazor/components/js-spa-frameworks?view=aspnetcore-8.0&preserve-view=true#blazor-web-app-registration): Covers root component custom element registration with `RegisterCustomElement`. * [Prefix for Blazor WebAssembly assets](xref:blazor/fundamentals/static-files?view=aspnetcore-8.0&preserve-view=true#prefix-for-blazor-webassembly-assets): Covers control of the path string that indicates the prefix for Blazor WebAssembly assets. -* [Temporary redirection URL validity duration](xref:blazor/security/server/index?view=aspnetcore-8.0&preserve-view=true#temporary-redirection-url-validity-duration): Covers control of the lifetime of data protection validity for temporary redirection URLs emitted by Blazor server-side rendering. +* [Temporary redirection URL validity duration](xref:blazor/security/index?view=aspnetcore-8.0&preserve-view=true#temporary-redirection-url-validity-duration): Covers control of the lifetime of data protection validity for temporary redirection URLs emitted by Blazor server-side rendering. * [Detailed errors](xref:blazor/fundamentals/handle-errors?view=aspnetcore-8.0&preserve-view=true#detailed-errors-for-razor-component-server-side-rendering): Covers enabling detailed errors for Razor component server-side rendering. * [Prerendering configuration](xref:blazor/components/render-modes?view=aspnetcore-8.0&preserve-view=true#prerendering): Prerendering is enabled by default for Blazor Web Apps. Follow this link for guidance on how to disable prerendering if you have special circumstances that require an app to disable prerendering. * [Form binding options](xref:blazor/forms/binding?view=aspnetcore-8.0&preserve-view=true#additional-binding-options): Covers form binding options configuration. @@ -556,7 +556,7 @@ For more information, see the following resources: * [`AuthenticationStateProvider` service](xref:blazor/security/index#authenticationstateprovider-service) * [Expose the authentication state as a cascading parameter](xref:blazor/security/index#expose-the-authentication-state-as-a-cascading-parameter) * [Customize unauthorized content with the Router component](xref:blazor/security/index#customize-unauthorized-content-with-the-router-component) -* +* ### New article on HTTP caching issues diff --git a/aspnetcore/release-notes/aspnetcore-8.0.md b/aspnetcore/release-notes/aspnetcore-8.0.md index 1fbf19992879..cb471bae91df 100644 --- a/aspnetcore/release-notes/aspnetcore-8.0.md +++ b/aspnetcore/release-notes/aspnetcore-8.0.md @@ -141,7 +141,7 @@ public HttpContext? HttpContext { get; set; } Accessing the from a static server component might be useful for inspecting and modifying headers or other properties. -For an example that passes state, access and refresh tokens, to components, see . +For an example that passes state, access and refresh tokens, to components, see . ### Render Razor components outside of ASP.NET Core @@ -302,7 +302,7 @@ Blazor supports generating a full Blazor-based Identity UI when you choose the a For more information, see the following resources: -* +* * [What's new with identity in .NET 8 (blog post)](https://devblogs.microsoft.com/dotnet/whats-new-with-identity-in-dotnet-8/#the-blazor-identity-ui) ### Secure Blazor WebAssembly with ASP.NET Core Identity @@ -311,7 +311,7 @@ The Blazor documentation hosts a new article and sample app to cover securing a For more information, see the following resources: -* +* * [What's new with identity in .NET 8 (blog post)](https://devblogs.microsoft.com/dotnet/whats-new-with-identity-in-dotnet-8/#the-blazor-identity-ui) ### Blazor Server with Yarp routing diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md index 71e944416f25..e9486ecfc532 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/blazor.md @@ -78,10 +78,10 @@ This works well if you've started from the Blazor Web App project template and s By default, the API only serializes the server-side name and role claims for access in the browser. An option can be passed to to include all claims. -For more information, see the following sections of : +For more information, see the following sections of : -* [Blazor Identity UI (Individual Accounts)](xref:blazor/security/server/index?view=aspnetcore-9.0#blazor-identity-ui-individual-accounts) -* [Manage authentication state in Blazor Web Apps](xref:blazor/security/server/index?view=aspnetcore-9.0#manage-authentication-state-in-blazor-web-apps) +* [Blazor Identity UI (Individual Accounts)](xref:blazor/security/index?view=aspnetcore-9.0#blazor-identity-ui-individual-accounts) +* [Manage authentication state in Blazor Web Apps](xref:blazor/security/index?view=aspnetcore-9.0#manage-authentication-state-in-blazor-web-apps) ### Add static server-side rendering (SSR) pages to a globally-interactive Blazor Web App @@ -152,7 +152,7 @@ For more information, see ` of the origin from which the app is served when compression is enabled or when a configuration for the WebSocket context is provided. -Compression can be disabled by setting `ConfigureWebSocketOptions` to `null`, which reduces the [vulnerability of the app to attack](xref:blazor/security/server/interactive-server-side-rendering#interactive-server-components-with-websocket-compression-enabled) but may result in reduced performance: +Compression can be disabled by setting `ConfigureWebSocketOptions` to `null`, which reduces the [vulnerability of the app to attack](xref:blazor/security/interactive-server-side-rendering#interactive-server-components-with-websocket-compression-enabled) but may result in reduced performance: ```csharp .AddInteractiveServerRenderMode(o => o.ConfigureWebSocketOptions = null) @@ -167,7 +167,7 @@ Configure a stricter `frame-ancestors` CSP with a value of `'none'` (single quot For more information, see the following resources: * -* +* ### Handle keyboard composition events in Blazor diff --git a/aspnetcore/security/authentication/accconfirm.md b/aspnetcore/security/authentication/accconfirm.md index c51718eeed7f..ac19e4377f13 100644 --- a/aspnetcore/security/authentication/accconfirm.md +++ b/aspnetcore/security/authentication/accconfirm.md @@ -19,7 +19,10 @@ This tutorial shows how to build an ASP.NET Core app with email confirmation and :::moniker range=">= aspnetcore-8.0" -For Blazor guidance, which adds to or supersedes the guidance in this article, see . +For Blazor guidance, which adds to or supersedes the guidance in this article, see the following resources: + +* +* :::moniker-end diff --git a/aspnetcore/security/authentication/identity-api-authorization.md b/aspnetcore/security/authentication/identity-api-authorization.md index ba4b53a0dca7..f1930e4b4dbd 100644 --- a/aspnetcore/security/authentication/identity-api-authorization.md +++ b/aspnetcore/security/authentication/identity-api-authorization.md @@ -15,7 +15,7 @@ uid: security/authentication/identity/spa [ASP.NET Core Identity](xref:security/authentication/identity) provides APIs that handle authentication, authorization, and identity management. The APIs make it possible to secure endpoints of a Web API backend with cookie-based authentication. A token-based option is available for clients that can't use cookies, but in using this you are responsible for ensuring the tokens are kept secure. We recommend using cookies for browser-based applications, because, by default, the browser automatically handles them without exposing them to JavaScript. -This article shows how to use Identity to secure a Web API backend for SPAs such as Angular, React, and Vue apps. The same backend APIs can be used to secure [Blazor WebAssembly apps](xref:blazor/security/webassembly/standalone-with-identity). +This article shows how to use Identity to secure a Web API backend for SPAs such as Angular, React, and Vue apps. The same backend APIs can be used to secure [Blazor WebAssembly apps](xref:blazor/security/webassembly/standalone-with-identity/index). ## Prerequisites diff --git a/aspnetcore/security/authentication/identity-enable-qrcodes.md b/aspnetcore/security/authentication/identity-enable-qrcodes.md index 892835bd7373..754222afd22c 100644 --- a/aspnetcore/security/authentication/identity-enable-qrcodes.md +++ b/aspnetcore/security/authentication/identity-enable-qrcodes.md @@ -17,7 +17,7 @@ ASP.NET Core ships with support for authenticator applications for individual au :::moniker range=">= aspnetcore-8.0" -The ASP.NET Core web app templates support authenticators but don't provide support for QR code generation. QR code generators ease the setup of 2FA. This document provides guidance for Razor Pages and MVC apps on how to add [QR code](https://wikipedia.org/wiki/QR_code) generation to the 2FA configuration page. For guidance that applies to Blazor Web Apps, see . +The ASP.NET Core web app templates support authenticators but don't provide support for QR code generation. QR code generators ease the setup of 2FA. This document provides guidance for Razor Pages and MVC apps on how to add [QR code](https://wikipedia.org/wiki/QR_code) generation to the 2FA configuration page. For guidance that applies to Blazor Web Apps, see . :::moniker-end diff --git a/aspnetcore/security/authentication/scaffold-identity.md b/aspnetcore/security/authentication/scaffold-identity.md index cc17a4c12da2..73194d77bcd2 100644 --- a/aspnetcore/security/authentication/scaffold-identity.md +++ b/aspnetcore/security/authentication/scaffold-identity.md @@ -24,7 +24,7 @@ Although the scaffolder generates the necessary C# code to scaffold Identity int Inspect the changes after running the Identity scaffolder. We recommend using GitHub or another source control system that shows file changes with a revert changes feature. -Services are required when using [two-factor authentication (2FA)](xref:blazor/security/server/qrcodes-for-authenticator-apps), [account confirmation and password recovery](xref:blazor/security/server/account-confirmation-and-password-recovery), and other security features with Identity. Services or service stubs aren't generated when scaffolding Identity. Services to enable these features must be added manually. +Services are required when using [two-factor authentication (2FA)](xref:blazor/security/qrcodes-for-authenticator-apps), [account confirmation and password recovery](xref:blazor/security/account-confirmation-and-password-recovery), and other security features with Identity. Services or service stubs aren't generated when scaffolding Identity. Services to enable these features must be added manually. ## Razor Pages and MVC Identity scaffolding diff --git a/aspnetcore/security/authentication/social/additional-claims.md b/aspnetcore/security/authentication/social/additional-claims.md index 2f58553a28c6..5b31ededf23c 100644 --- a/aspnetcore/security/authentication/social/additional-claims.md +++ b/aspnetcore/security/authentication/social/additional-claims.md @@ -91,7 +91,7 @@ The sample app saves the access token in `OnPostConfirmationAsync` (new user reg [!code-csharp[](additional-claims/samples/6.x/ClaimsSample/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs?name=snippet_OnPostConfirmationAsync&highlight=49-53,73)] > [!NOTE] -> For information on passing tokens to the Razor components of a server-side Blazor app, see . +> For information on passing tokens to the Razor components of a server-side Blazor app, see . ## How to add additional custom tokens @@ -259,7 +259,7 @@ The sample app saves the access token in `OnPostConfirmationAsync` (new user reg [!code-csharp[](additional-claims/samples/3.x/ClaimsSample/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs?name=snippet_OnPostConfirmationAsync&highlight=54-56)] > [!NOTE] -> For information on passing tokens to the Razor components of a server-side Blazor app, see . +> For information on passing tokens to the Razor components of a server-side Blazor app, see . ## How to add additional custom tokens diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index 0dc8b8a8ca05..fec034ecb636 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -602,26 +602,16 @@ items: uid: blazor/security/index - name: Authentication state uid: blazor/security/authentication-state - - name: Server - items: - - name: Overview - uid: blazor/security/server/index - - name: Static server-side rendering threats - uid: blazor/security/server/static-server-side-rendering - - name: Interactive server-side rendering threats - uid: blazor/security/server/interactive-server-side-rendering - - name: Account confirmation and password recovery - uid: blazor/security/server/account-confirmation-and-password-recovery - - name: QR code generation - uid: blazor/security/server/qrcodes-for-authenticator-apps - - name: Additional scenarios - uid: blazor/security/server/additional-scenarios - name: Blazor WebAssembly items: - name: Overview uid: blazor/security/webassembly/index - name: Standalone with Identity - uid: blazor/security/webassembly/standalone-with-identity + items: + - name: Overview + uid: blazor/security/webassembly/standalone-with-identity/index + - name: Account confirmation and password recovery + uid: blazor/security/webassembly/standalone-with-identity/account-confirmation-and-password-recovery - name: Standalone with Authentication library uid: blazor/security/webassembly/standalone-with-authentication-library - name: Standalone with Microsoft Accounts @@ -648,8 +638,18 @@ items: uid: blazor/security/blazor-web-app-entra - name: Blazor Web App with OIDC uid: blazor/security/blazor-web-app-oidc + - name: Static server-side rendering threats + uid: blazor/security/static-server-side-rendering + - name: Interactive server-side rendering threats + uid: blazor/security/interactive-server-side-rendering + - name: Account confirmation and password recovery + uid: blazor/security/account-confirmation-and-password-recovery + - name: QR code generation + uid: blazor/security/qrcodes-for-authenticator-apps - name: Content Security Policy uid: blazor/security/content-security-policy + - name: Server-side and Blazor Web App additional scenarios + uid: blazor/security/additional-scenarios - name: State management uid: blazor/state-management - name: Debug diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod0.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod0.md index d51da79cb252..fd4af396428a 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod0.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod0.md @@ -50,7 +50,7 @@ Welcome to what's new in the ASP.NET Core docs for June 2024. This article lists - - Import-Export interop: collocated JS with RCL - - Use 'reconnection UI' for all references - - Interactive SSR RCs in global WASM/Auto projects -- - Simplified auth state serialization for BWAs +- - Simplified auth state serialization for BWAs - - Change Tooling article content layout - - Blazor CLI commands moving to `dotnet watch` diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod1.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod1.md index f1f315a8435e..37021bb58914 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod1.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod1.md @@ -13,7 +13,7 @@ Welcome to what's new in the ASP.NET Core docs for January 2024. This article li ### New articles -- +- ### Updated articles @@ -42,14 +42,14 @@ Welcome to what's new in the ASP.NET Core docs for January 2024. This article li - - Blazor 8.0 content updates - Custom Blazor WASM loading progress indicator -- +- - Blazor 8.0 content updates - Improve auth state provider guidance - Add Identity BWA template cross-links - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) -- - Content follow-up updates (8.0) +- - Content follow-up updates (8.0) - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) @@ -59,7 +59,7 @@ Welcome to what's new in the ASP.NET Core docs for January 2024. This article li - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) - - Content follow-up updates (8.0) -- - Add troubleshooting guidance +- - Add troubleshooting guidance - - Groups/roles article and Graph article updates - "Base address" clarifications for `HttpClient` diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod2.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod2.md index 1af658f3c6d1..20fd67d912d6 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod2.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod2.md @@ -59,7 +59,7 @@ Welcome to what's new in the ASP.NET Core docs for February 2024. This article l - Add WebAssembly (runtime) startup callbacks - Blazor Startup - sample environment variable name - Startup - manually start Standalone Blazor WebAssembly -- - Add roles and test user guidance +- - Add roles and test user guidance - - [Blazor] SignalR - remove "using System" reminder - - Update 'Blazor Server' references - diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod3.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod3.md index 72e98b6c80ed..281cf2e8f84e 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod3.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod3.md @@ -49,7 +49,7 @@ Welcome to what's new in the ASP.NET Core docs for March 2024. This article list - - [Blazor] Event handling - ParentChild2.razor without Task.Yield() - [Blazor] Event handling - first InvokeAsync example with args -- - Dependency on DBContext for Blazor Identity UI +- - Dependency on DBContext for Blazor Identity UI - - Updates to 'click'-based remarks - Server-side behaviors during static SSR @@ -62,7 +62,7 @@ Welcome to what's new in the ASP.NET Core docs for March 2024. This article list - WebSocket compression/CSP and security guidance - - Server-side behaviors during static SSR - - Server-side behaviors during static SSR -- - Temporarily surface PU issue for access tokens +- - Temporarily surface PU issue for access tokens - - Blazor WASM cookie security for web APIs - PATCH section and other updates @@ -80,9 +80,9 @@ Welcome to what's new in the ASP.NET Core docs for March 2024. This article list - - Blazor-specific 'how to download' guidance - - Blazor-specific 'how to download' guidance - - Blazor - data-binding - event fix -- - WebSocket compression/CSP and security guidance +- - WebSocket compression/CSP and security guidance - - Add BWA global Auto approach -- - WASM+Identity same-site & antiforgery updates +- - WASM+Identity same-site & antiforgery updates ## Fundamentals diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod4.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod4.md index d93a609a8486..74ea7b61e254 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod4.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod4.md @@ -15,7 +15,7 @@ Welcome to what's new in the ASP.NET Core docs for April 2024. This article list - - -- +- - ### Updated articles @@ -60,7 +60,7 @@ Welcome to what's new in the ASP.NET Core docs for April 2024. This article list - - OIDC Blazor authentication text improvements - Clarify use of IHttpContextAccessor/HttpContext -- - Shorten class name +- - Shorten class name ## Fundamentals diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md index d2138cd22890..aa3f2c60f046 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md @@ -44,7 +44,7 @@ Welcome to what's new in the ASP.NET Core docs for May 2024. This article lists - - BlazorWebAppOidcBff Aspire article updates - Add product unit issue cross-link -- - Update ref source links to Blazor security API +- - Update ref source links to Blazor security API - - Add OverscanCount to ref article - - More hints on interactivity for doc components - - Update "CLI" tab controls