From 95e2d8b72edfcd101133de5b8bf4807b6979699f Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:44:16 -0500 Subject: [PATCH 1/3] [10.0] API doc cross-links --- aspnetcore/blazor/call-web-api.md | 8 +- aspnetcore/blazor/components/integration.md | 4 +- aspnetcore/blazor/components/quickgrid.md | 13 +--- aspnetcore/blazor/forms/input-components.md | 6 +- aspnetcore/blazor/forms/validation.md | 19 +++-- .../call-javascript-from-dotnet.md | 31 ++++---- .../javascript-interoperability/index.md | 8 +- .../prerendered-state-persistence.md | 20 +++-- aspnetcore/blazor/state-management/server.md | 14 ++-- .../fundamentals/openapi/customize-openapi.md | 6 +- .../authentication/passkeys/blazor.md | 14 +--- .../security/authentication/passkeys/index.md | 76 +++++++------------ 12 files changed, 81 insertions(+), 138 deletions(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index c1a230e81713..3aa3342a7fe9 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -851,9 +851,7 @@ To simplify the creation of PATCH documents in the app issuing PATCH requests, a :::moniker range=">= aspnetcore-10.0" - - -Install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package and use the API features of the package to compose a `JsonPatchDocument` for a PATCH request. +Install the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package and use the API features of the package to compose a for a PATCH request. [!INCLUDE[](~/includes/package-reference.md)] @@ -924,11 +922,9 @@ Follow the guidance in the article to add a PATCH contr :::moniker range=">= aspnetcore-10.0" - - Add a package reference for the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson) NuGet package to the web API app. -In the `Program` file add an `@using` directive for the `Microsoft.AspNetCore.JsonPatch.SystemTextJson` namespace: +In the `Program` file add an `@using` directive for the namespace: ```csharp using Microsoft.AspNetCore.JsonPatch.SystemTextJson; diff --git a/aspnetcore/blazor/components/integration.md b/aspnetcore/blazor/components/integration.md index 2ce602591be3..15830b17f28f 100644 --- a/aspnetcore/blazor/components/integration.md +++ b/aspnetcore/blazor/components/integration.md @@ -406,9 +406,7 @@ In `Pages/_Host.cshtml` of Blazor apps that are `ServerPrerendered` in a Blazor :::moniker range=">= aspnetcore-10.0" - - -Decide what state to persist using the service. The `[PersistentState]` attribute applied to a property registers a callback to persist the state during prerendering and loads it when the component renders interactively or the service is instantiated. +Decide what state to persist using the service. The [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) applied to a property registers a callback to persist the state during prerendering and loads it when the component renders interactively or the service is instantiated. In the following example, the `{TYPE}` placeholder represents the type of data to persist (for example, `WeatherForecast[]`). diff --git a/aspnetcore/blazor/components/quickgrid.md b/aspnetcore/blazor/components/quickgrid.md index 3b8912038169..103ed107b641 100644 --- a/aspnetcore/blazor/components/quickgrid.md +++ b/aspnetcore/blazor/components/quickgrid.md @@ -195,9 +195,7 @@ QuickGrid also supports passing custom attributes and style classes ( - -Apply a stylesheet class to a row of the grid based on the row item using the `RowClass` parameter. +Apply a stylesheet class to a row of the grid based on the row item using the parameter. In the following example: @@ -219,16 +217,11 @@ In the following example: :::moniker-end - :::moniker range=">= aspnetcore-10.0" - - -:::moniker-end - ### Close `QuickGrid` column options -Close the `QuickGrid` column options UI with the `HideColumnOptionsAsync` method. +Close the `QuickGrid` column options UI with the method. The following example closes the column options UI as soon as the title filter is applied: @@ -253,6 +246,8 @@ The following example closes the column options UI as soon as the title filter i } ``` +:::moniker-end + ## Entity Framework Core (EF Core) data source Use the factory pattern to resolve an EF Core database context that provides data to a `QuickGrid` component. For more information on why the factory pattern is recommended, see . diff --git a/aspnetcore/blazor/forms/input-components.md b/aspnetcore/blazor/forms/input-components.md index aee9592646c2..8dcc271dc3d3 100644 --- a/aspnetcore/blazor/forms/input-components.md +++ b/aspnetcore/blazor/forms/input-components.md @@ -458,7 +458,7 @@ The validation summary displays the friendly name when the field's value is inva > The Production Date field must be a date. - - -The `InputHidden` component provides a hidden input field for storing string values. +The [`InputHidden` component](xref:Microsoft.AspNetCore.Components.Forms.InputHidden) provides a hidden input field for storing string values. In the following example, a hidden input field is created for the form's `Parameter` property. When the form is submitted, the value of the hidden field is displayed: diff --git a/aspnetcore/blazor/forms/validation.md b/aspnetcore/blazor/forms/validation.md index de5d8dff4e5f..8e7a41cc6725 100644 --- a/aspnetcore/blazor/forms/validation.md +++ b/aspnetcore/blazor/forms/validation.md @@ -1574,16 +1574,17 @@ The doesn't work w ## Use validation models from a different assembly - - For model validation defined in a different assembly, such as a library or the `.Client` project of a Blazor Web App: * If the library is a plain class library (it isn't based on the `Microsoft.NET.Sdk.Web` or `Microsoft.NET.Sdk.Razor` SDKs), add a package reference to the library for the [`Microsoft.Extensions.Validation` NuGet package](https://www.nuget.org/packages/Microsoft.Extensions.Validation). -* Create a method in the library or `.Client` project that receives an instance as an argument and calls `AddValidation` on it. -* In the app, call both the method and `AddValidation`. +* Create a method in the library or `.Client` project that receives an instance as an argument and calls on it. +* In the app, call both the method and . The preceding approach results in validation of the types from both assemblies. + + In the following example, the `AddValidationForTypesInClient` method is created for the `.Client` project of a Blazor Web App for validation using types defined in the `.Client` project. `ServiceCollectionExtensions.cs` (in the `.Client` project): @@ -1601,7 +1602,7 @@ public static class ServiceCollectionExtensions } ``` -In the server project's `Program` file, add the namespace and call the `.Client` project's service collection extension method (`AddValidationForTypesInClient`) and `AddValidation`: +In the server project's `Program` file, add the namespace and call the `.Client` project's service collection extension method (`AddValidationForTypesInClient`) and : ```csharp using BlazorSample.Client.Extensions; @@ -1624,11 +1625,9 @@ To create a validated form, use a - -1. Call the `AddValidation` extension method in the `Program` file where services are registered. +1. Call the extension method in the `Program` file where services are registered. 2. Declare the form model types in a C# class file, not in a Razor component (`.razor`). -3. Annotate the root form model type with the `[ValidatableType]` attribute. +3. Annotate the root form model type with the [`[ValidatableType]` attribute](xref:Microsoft.Extensions.Validation.ValidatableTypeAttribute). Without following the preceding steps, form validation behavior doesn't include nested model and collection type validation. @@ -1636,7 +1635,7 @@ Without following the preceding steps, form validation behavior doesn't include The following example demonstrates customer orders with the improved form validation (details omitted for brevity): -In `Program.cs`, call `AddValidation` on the service collection: +In `Program.cs`, call on the service collection: ```csharp builder.Services.AddValidation(); diff --git a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md index e37535516a31..8fe37c5fdd9f 100644 --- a/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md +++ b/aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md @@ -461,14 +461,13 @@ IJSRuntime JS { get; set; } :::moniker range=">= aspnetcore-10.0" - + ## Create an instance of a JS object using a constructor function Create an instance of a JS object using a constructor function and get the / .NET handle for referencing the instance with the following API: -* `InvokeConstructorAsync` (asynchronous) +* (asynchronous) * `InvokeConstructor` (synchronous) Examples in this section demonstrate the API calls with the following `TestClass` with a constructor function (`constructor(text)`): @@ -487,7 +486,7 @@ window.TestClass = class { ### Asynchronous `InvokeConstructorAsync` -Use `InvokeConstructorAsync(string identifier, object?[]? args)` on and to invoke the specified JS constructor function asynchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an . +Use on and to invoke the specified JS constructor function asynchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an . ```csharp var classRef = await JSRuntime.InvokeConstructorAsync("TestClass", "Blazor!"); @@ -499,7 +498,7 @@ An overload is available that takes a ### Synchronous `InvokeConstructor` -Use `InvokeConstructor(string identifier, object?[]? args)` on and to invoke the specified JS constructor function synchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an : +Use `InvokeConstructor` on and to invoke the specified JS constructor function synchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an : ```csharp var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); @@ -514,7 +513,7 @@ An overload is available that takes a Read or modify the value of a JS object property, both data and accessor properties, with the following API: -* `GetValueAsync`/`SetValueAsync` (asynchronous) +* / (asynchronous) * `GetValue`/`SetValue` (synchronous) Examples in this section demonstrate the API calls with the following JS object (`testObject`): @@ -527,14 +526,14 @@ window.testObject = { ### Asynchronous `GetValueAsync` and `SetValueAsync` -Use `GetValueAsync(string identifier)` to read the value of the specified JS property asynchronously. A is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataPropertyAsync`: +Use to read the value of the specified JS property asynchronously. A is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataPropertyAsync`: ```csharp var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync("testObject.num"); ``` -Use `SetValueAsync(string identifier, TValue value)` to update the value of the specified JS property asynchronously. If the property isn't defined on the target object, the property is created. A is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`: +Use to update the value of the specified JS property asynchronously. If the property isn't defined on the target object, the property is created. A is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`: ```csharp await JSRuntime.SetValueAsync("testObject.num", 20); @@ -543,14 +542,14 @@ await JSRuntime.SetValueAsync("testObject.num2", 30); ### Synchronous `GetValue` and `SetValue` -Use `GetValue(string identifier)` to read the value of the specified JS property synchronously. A is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataProperty`: +Use `GetValue` to read the value of the specified JS property synchronously. A is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataProperty`: ```csharp var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var valueFromDataProperty = inProcRuntime.GetValue("testObject.num"); ``` -Use `SetValue(string identifier, TValue value)` to update the value of the specified JS property synchronously. The property can't be a `get`-only property. If the property isn't defined on the target object, the property is created. A is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`: +Use `SetValue` to update the value of the specified JS property synchronously. The property can't be a `get`-only property. If the property isn't defined on the target object, the property is created. A is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`: ```csharp var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); @@ -630,16 +629,16 @@ In server-side scenarios, JS interop calls can't be issued after Blazor's Signal :::moniker range=">= aspnetcore-10.0" - + * JS interop method calls * * * - * `InvokeConstructorAsync` - * `GetValueAsync` - * `SetValueAsync` -* `Dispose`/`DisposeAsync` calls on any . + * + * + * +* /`DisposeAsync` calls on any . :::moniker-end @@ -649,7 +648,7 @@ In server-side scenarios, JS interop calls can't be issued after Blazor's Signal * * * -* `Dispose`/`DisposeAsync` calls on any . +* /`DisposeAsync` calls on any . :::moniker-end diff --git a/aspnetcore/blazor/javascript-interoperability/index.md b/aspnetcore/blazor/javascript-interoperability/index.md index 9261125d78d1..a4a0e7785b9b 100644 --- a/aspnetcore/blazor/javascript-interoperability/index.md +++ b/aspnetcore/blazor/javascript-interoperability/index.md @@ -331,15 +331,15 @@ JavaScript (JS) interop calls can't be issued after Blazor's SignalR circuit is :::moniker range=">= aspnetcore-10.0" - + * JS interop method calls * * * - * `InvokeConstructorAsync` - * `GetValueAsync` - * `SetValueAsync` + * + * + * * `Dispose`/`DisposeAsync` calls on any . :::moniker-end diff --git a/aspnetcore/blazor/state-management/prerendered-state-persistence.md b/aspnetcore/blazor/state-management/prerendered-state-persistence.md index 17742bfa1e15..ce770852cfc1 100644 --- a/aspnetcore/blazor/state-management/prerendered-state-persistence.md +++ b/aspnetcore/blazor/state-management/prerendered-state-persistence.md @@ -38,15 +38,13 @@ The persisted prerendered state is transferred to the client, where it's used to :::moniker range=">= aspnetcore-10.0" - - -To preserve prerendered state, use the `[PersistentState]` attribute to persist state in properties. Properties with this attribute are automatically persisted using the service during prerendering. The state is retrieved when the component renders interactively or the service is instantiated. +To preserve prerendered state, use the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) to persist state in properties. Properties with this attribute are automatically persisted using the service during prerendering. The state is retrieved when the component renders interactively or the service is instantiated. By default, properties are serialized using the serializer with default settings and persisted in the prerendered HTML. Serialization isn't trimmer safe and requires preservation of the types used. For more information, see . The following counter component persists counter state during prerendering and retrieves the state to initialize the component: -* The `[PersistentState]` attribute is applied to the nullable `int` type (`CurrentCount`). +* The [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) is applied to the nullable `int` type (`CurrentCount`). * The counter's state is assigned when `null` in `OnInitialized` and restored automatically when the component renders interactively. `PrerenderedCounter2.razor`: @@ -96,7 +94,7 @@ When the component executes, `CurrentCount` is only set once during prerendering In the following example that serializes state for multiple components of the same type: -* Properties annotated with the `[PersistentState]` attribute are serialized during prerendering. +* Properties annotated with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) are serialized during prerendering. * The [`@key` directive attribute](xref:blazor/components/key#use-of-the-key-directive-attribute) is used to ensure that the state is correctly associated with the component instance. * The `Element` property is initialized in the [`OnInitialized` lifecycle method](xref:blazor/components/lifecycle#component-initialization-oninitializedasync) to avoid null reference exceptions, similarly to how null references are avoided for query parameters and form data. @@ -144,12 +142,12 @@ In the following example that serializes state for multiple components of the sa In the following example that serializes state for a dependency injection service: -* Properties annotated with the `[PersistentState]` attribute are serialized during prerendering and deserialized when the app becomes interactive. +* Properties annotated with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) are serialized during prerendering and deserialized when the app becomes interactive. * The extension method is used to register the service for persistence. The render mode is required because the render mode can't be inferred from the service type. Use any of the following values: * `RenderMode.Server`: The service is available for the Interactive Server render mode. * `RenderMode.Webassembly`: The service is available for the Interactive Webassembly render mode. * `RenderMode.InteractiveAuto`: The service is available for both the Interactive Server and Interactive Webassembly render modes if a component renders in either of those modes. -* The service is resolved during the initialization of an interactive render mode, and the properties annotated with the `[PersistentState]` attribute are deserialized. +* The service is resolved during the initialization of an interactive render mode, and the properties annotated with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) are deserialized. > [!NOTE] > Only persisting scoped services is supported. @@ -161,7 +159,7 @@ Serialized properties are identified from the actual service instance: * Supports shared code in different assemblies. * Results in each instance exposing the same properties. -The following counter service, `CounterTracker`, marks its current count property, `CurrentCount` with the `[PersistentState]` attribute. The property is serialized during prerendering and deserialized when the app becomes interactive wherever the service is injected. +The following counter service, `CounterTracker`, marks its current count property, `CurrentCount` with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute). The property is serialized during prerendering and deserialized when the app becomes interactive wherever the service is injected. `CounterTracker.cs`: @@ -233,7 +231,7 @@ To use preceding component to demonstrate persisting the count of 10 in `Counter ## Use the `PersistentComponentState` service directly instead of the declarative model -As an alternative to using the declarative model for persisting state with the `[PersistentState]` attribute, you can use the service directly, which offers greater flexibility for complex state persistence scenarios. Call to register a callback to persist the component state during prerendering. The state is retrieved when the component renders interactively. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown. +As an alternative to using the declarative model for persisting state with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute), you can use the service directly, which offers greater flexibility for complex state persistence scenarios. Call to register a callback to persist the component state during prerendering. The state is retrieved when the component renders interactively. Make the call at the end of initialization code in order to avoid a potential race condition during app shutdown. The following counter component example persists counter state during prerendering and retrieves the state to initialize the component. @@ -326,7 +324,7 @@ When the component executes, `currentCount` is only set once during prerendering ## Serialization extensibility for persistent component state - + Implement a custom serializer with the `IPersistentComponentStateSerializer` interface. Without a registered custom serializer, serialization falls back to the existing JSON serialization. @@ -362,7 +360,7 @@ For components embedded into a page or view of a Razor Pages or MVC app, you mus ## Interactive routing and prerendering - + When the `Routes` component doesn't define a render mode, the app is using per-page/component interactivity and navigation. Using per-page/component navigation, internal navigation is handled by [enhanced routing](xref:blazor/fundamentals/routing#enhanced-navigation-and-form-handling) after the app becomes interactive. "Internal navigation" in this context means that the URL destination of the navigation event is a Blazor endpoint inside the app. diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md index f59229f3ad09..c2d837d8a47f 100644 --- a/aspnetcore/blazor/state-management/server.md +++ b/aspnetcore/blazor/state-management/server.md @@ -51,8 +51,6 @@ An app can only persist *app state*. UIs can't be persisted, such as component i ## Circuit state persistence - - During server-side rendering, Blazor Web Apps can persist a user's session (circuit) state when the connection to the server is lost for an extended period of time or proactively paused, as long as a full-page refresh isn't triggered. This allows users to resume their session without losing unsaved work in the following scenarios: * Browser tab throttling @@ -85,8 +83,8 @@ State persistence is enabled by default when . The default is two hours. For example, use `TimeSpan.FromHours(3)` for a three-hour retention period. +* (`{CIRCUIT COUNT}` placeholder): The maximum number of circuits to retain. The default is 1,000 circuits. For example, use `2000` to retain state for up to 2,000 circuits. +* (`{RETENTION PERIOD}` placeholder): The maximum retention period as a . The default is two hours. For example, use `TimeSpan.FromHours(3)` for a three-hour retention period. ```csharp services.Configure(options => @@ -101,7 +99,7 @@ Persisting component state across circuits is built on top of the existing [NOTE] > Persisting component state for prerendering works for any interactive render mode, but circuit state persistence only works for the **Interactive Server** render mode. -Annotate component properties with `[PersistentState]` to enable circuit state persistence. The following example also keys the items with the [`@key` directive attribute](xref:blazor/components/key) to provide a unique identifier for each component instance: +Annotate component properties the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) to enable circuit state persistence. The following example also keys the items with the [`@key` directive attribute](xref:blazor/components/key) to provide a unique identifier for each component instance: ```razor @foreach (var item in Items) @@ -120,7 +118,7 @@ Annotate component properties with `[PersistentState]` to enable circuit state p } ``` -To persist state for scoped services, annotate service properties with `[PersistentState]`, add the service to the service collection, and call the extension method with the service: +To persist state for scoped services, annotate service properties with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute), add the service to the service collection, and call the extension method with the service: ```csharp public class CustomUserService @@ -137,9 +135,9 @@ services.AddRazorComponents() ``` > [NOTE] -> The preceding example persists `UserData` state when the service is used in component prerendering for both Interactive Server and Interactive WebAssembly rendering because `RenderMode.InteractiveAuto` is specified to `RegisterPersistentService`. However, circuit state persistence is only available for the **Interactive Server** render mode. +> The preceding example persists `UserData` state when the service is used in component prerendering for both Interactive Server and Interactive WebAssembly rendering because `RenderMode.InteractiveAuto` is specified to . However, circuit state persistence is only available for the **Interactive Server** render mode. -To handle distributed state persistence (and to act as the default state persistence mechanism when configured), assign a [`HybridCache`](xref:performance/caching/overview#hybridcache) (API: ) to the app, which configures its own persistence period (`PersistedCircuitDistributedRetentionPeriod`, eight hours by default). `HybridCache` is used because it provides a unified approach to distributed storage that doesn't require separate packages for each storage provider. +To handle distributed state persistence (and to act as the default state persistence mechanism when configured), assign a [`HybridCache`](xref:performance/caching/overview#hybridcache) (API: ) to the app, which configures its own persistence period (, eight hours by default). `HybridCache` is used because it provides a unified approach to distributed storage that doesn't require separate packages for each storage provider. In the following example, a is implemented with the [Redis](https://redis.io/) storage provider: diff --git a/aspnetcore/fundamentals/openapi/customize-openapi.md b/aspnetcore/fundamentals/openapi/customize-openapi.md index 63d6f53dfdd1..b6fba9722494 100644 --- a/aspnetcore/fundamentals/openapi/customize-openapi.md +++ b/aspnetcore/fundamentals/openapi/customize-openapi.md @@ -101,12 +101,8 @@ Operation transformers have access to a context object which contains: For example, the following operation transformer adds `500` as a response status code supported by all operations in the document. [!code-csharp[](~/fundamentals/openapi/samples/10.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)] - - -Operation transformers can also be added to specific endpoint with the `AddOpenApiOperationTransformer` API, instead of all endpoints in a document. This can be useful to change specific OpenAPI data for a specific endpoint, like adding a security scheme, response description or other OpenAPI operation properties. The following example demonstrates adding an operation transformer to a deprecated endpoint specifically, which marks the endpoint as deprecated in the OpenAPI document. +Operation transformers can also be added to specific endpoint with the API, instead of all endpoints in a document. This can be useful to change specific OpenAPI data for a specific endpoint, like adding a security scheme, response description or other OpenAPI operation properties. The following example demonstrates adding an operation transformer to a deprecated endpoint specifically, which marks the endpoint as deprecated in the OpenAPI document. [!code-csharp[](~/fundamentals/openapi/samples/10.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer2)] diff --git a/aspnetcore/security/authentication/passkeys/blazor.md b/aspnetcore/security/authentication/passkeys/blazor.md index ded8ce50e9ab..cb115d7b53e1 100644 --- a/aspnetcore/security/authentication/passkeys/blazor.md +++ b/aspnetcore/security/authentication/passkeys/blazor.md @@ -23,14 +23,8 @@ For an overview of passkeys and general configuration guidance, see - -[.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) - ## Create a Blazor Web App Use the following guidance to create a new Blazor Web App with ASP.NET Core Identity, which includes passkeys support. @@ -125,14 +119,8 @@ The following guidance relies upon an app that was created with **Individual Acc ## Prerequisites - - * An existing Blazor Web App (.NET 10 or later) with ASP.NET Core Identity -* [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) +* [.NET SDK](https://dotnet.microsoft.com/download) (.NET 10 or later) For migration guidance, see . diff --git a/aspnetcore/security/authentication/passkeys/index.md b/aspnetcore/security/authentication/passkeys/index.md index f2978551e6a3..838eb0b5e962 100644 --- a/aspnetcore/security/authentication/passkeys/index.md +++ b/aspnetcore/security/authentication/passkeys/index.md @@ -5,7 +5,7 @@ description: Discover how to enable Web Authentication API (WebAuthn) passkeys i ms.author: wpickett monikerRange: '>= aspnetcore-10.0' ms.custom: mvc -ms.date: 10/30/2025 +ms.date: 11/12/2025 uid: security/authentication/passkeys/index --- # Enable Web Authentication API (WebAuthn) passkeys @@ -17,8 +17,6 @@ uid: security/authentication/passkeys/index --> - - Passkeys provide a modern, phishing-resistant authentication method based on the [Web Authentication API (WebAuthn)](https://developer.mozilla.org/docs/Web/API/Web_Authentication_API) and [FIDO2](https://www.microsoft.com/security/business/security-101/what-is-fido2) standards. They are a secure alternative to passwords, using public key cryptography and device-based authentication. This article explains how to configure an ASP.NET Core app to use passkeys to authenticate users. For guidance specific to new and existing Blazor Web Apps, see after reading this article. @@ -78,13 +76,7 @@ Two fundamental processes underpin passkey operations: attestation and assertion ## Prerequisites - - -* [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) * A modern web browser that supports WebAuthn. * A device with a platform authenticator, such as Windows Hello or Apple secure enclave, or a security key. @@ -94,13 +86,13 @@ When implementing passkeys in ASP.NET Core Identity, ensure the app meets the se ### Host header validation -The implementation infers the Relying Party ID from the host header when `ServerDomain` isn't explicitly configured. The hosting environment must validate host headers to prevent credential-scoping attacks, which involve using compromised or stolen user credentials (usernames, passwords, tokens) to gain unauthorized access. +The implementation infers the Relying Party ID from the host header when isn't explicitly configured. The hosting environment must validate host headers to prevent credential-scoping attacks, which involve using compromised or stolen user credentials (usernames, passwords, tokens) to gain unauthorized access. -**Mitigation**: Either explicitly configure `ServerDomain` in `IdentityPasskeyOptions` or ensure that the hosting environment (Kestrel, IIS, reverse proxy) validates host headers. For configuration details, see your hosting platform's documentation. +**Mitigation**: Either explicitly configure in or ensure that the hosting environment (Kestrel, IIS, reverse proxy) validates host headers. For configuration details, see your hosting platform's documentation. ### Subdomain security -ASP.NET Core's passkeys implementation handles subdomain security through the `ServerDomain` configuration option. When `ServerDomain` isn't explicitly specified, the implementation uses the host header to determine the domain. This means that ***the page on which the passkey was registered controls the domain*** for that credential. +ASP.NET Core's passkeys implementation handles subdomain security through the configuration option. When isn't explicitly specified, the implementation uses the host header to determine the domain. This means that ***the page on which the passkey was registered controls the domain*** for that credential. For example: @@ -108,7 +100,7 @@ For example: * If registered on `contoso.com`, it also works on `*.contoso.com`. * The browser enforces that passkeys can only be used on the domain (and subdomains) where they were registered. -**Requirement**: Apps requiring strict domain control should explicitly set `ServerDomain` rather than relying on the host header. Don't serve untrusted content on any subdomain within the `ServerDomain` scope. If you can't guarantee this, implement [custom origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin) to restrict passkey usage to specific origins. +**Requirement**: Apps requiring strict domain control should explicitly set rather than relying on the host header. Don't serve untrusted content on any subdomain within the scope. If you can't guarantee this, implement [custom origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin) to restrict passkey usage to specific origins. ### HTTPS requirement @@ -127,7 +119,7 @@ For applications implementing passkey-only authentication, consider: * Recovery codes generated during account creation. * Email-based recovery flows. * Mandatory registration of multiple passkeys. -* Monitoring the `IsBackedUp` flag on `UserPasskeyInfo` to prompt users to add additional credentials. +* Monitoring the flag on to prompt users to add additional credentials. ### Administrative controls @@ -158,11 +150,11 @@ The Blazor Web App template enforces these limits by default at the application ## Configure passkey options -ASP.NET Core Identity provides various options to configure passkey behavior through the `IdentityPasskeyOptions` class, which include: +ASP.NET Core Identity provides various options to configure passkey behavior through the class, which include: -* `AuthenticatorTimeout`: Gets or sets the time that the browser should wait for the authenticator to provide a passkey as a . This option applies to both creating a new passkey and requesting an existing passkey. This option is treated as a hint to the browser, and the browser may ignore the option. The default value is 5 minutes. -* `ChallengeSize`: Gets or sets the size of the challenge in bytes sent to the client during attestation and assertion. This option applies to both creating a new passkey and requesting an existing passkey. The default value is 32 bytes. -* `ServerDomain`: Gets or sets the effective Relying Party ID (domain) of the server. This should be unique and will be used as the identity for the server. This option applies to both creating a new passkey and requesting an existing passkey. If `null`, which is the default value, the server's origin is used. For more information, see [Relying Party Identifier RP ID](https://www.w3.org/TR/webauthn-3/#rp-id). +* : Gets or sets the time that the browser should wait for the authenticator to provide a passkey as a . This option applies to both creating a new passkey and requesting an existing passkey. This option is treated as a hint to the browser, and the browser may ignore the option. The default value is 5 minutes. +* : Gets or sets the size of the challenge in bytes sent to the client during attestation and assertion. This option applies to both creating a new passkey and requesting an existing passkey. The default value is 32 bytes. +* : Gets or sets the effective Relying Party ID (domain) of the server. This should be unique and will be used as the identity for the server. This option applies to both creating a new passkey and requesting an existing passkey. If `null`, which is the default value, the server's origin is used. For more information, see [Relying Party Identifier RP ID](https://www.w3.org/TR/webauthn-3/#rp-id). Example configuration: @@ -175,20 +167,11 @@ builder.Services.Configure(options => }); ``` - - -For a complete list of configuration options during the .NET 10 preview release period, see the [`IdentityPasskeyOptions` reference source (`dotnet/aspnetcore` GitHub repository)](https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Core/src/IdentityPasskeyOptions.cs). +For a complete list of configuration options, see . For the most up-to-date browser defaults, see the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/). > [!NOTE] > Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next preview release of .NET. To select a tag for a specific release, use the **Switch branches or tags** dropdown list. For more information, see [How to select a version tag of ASP.NET Core source code (`dotnet/AspNetCore.Docs` #26205)](https://github.com/dotnet/AspNetCore.Docs/discussions/26205). -> [!NOTE] -> The browser defaults mentioned in the API documentation were valid as of August, 2025. See the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/) for the most up-to-date defaults. - ## Custom attestation statement validation By default, ASP.NET Core Identity doesn't validate attestation statements. This is suitable for most consumer authentication scenarios. If your app requires verification of authenticator properties or if you want to disallow specific authenticators from being used, for example, in enterprise environments that require a higher level of security, you can implement custom attestation validation: @@ -309,11 +292,11 @@ app.MapPost("/Account/PasskeyCreationOptions", async ( }); ``` -The `MakePasskeyCreationOptionsAsync` method is central to this process. The method accepts a `PasskeyUserEntity` that describes the user for whom the passkey is being created. This entity contains the user's ID, username (typically an email address), and a human-readable display name. The method returns a JSON string that conforms to the WebAuthn `PublicKeyCredentialCreationOptions` schema, which the browser uses in the next step. Behind the scenes, this method also stores temporary state in an authentication cookie to ensure that the response from the browser corresponds to these specific options. +The method is central to this process. The method accepts a `PasskeyUserEntity` that describes the user for whom the passkey is being created. This entity contains the user's ID, username (typically an email address), and a human-readable display name. The method returns a JSON string that conforms to the WebAuthn `PublicKeyCredentialCreationOptions` schema, which the browser uses in the next step. Behind the scenes, this method also stores temporary state in an authentication cookie to ensure that the response from the browser corresponds to these specific options. ### Step 3: Server generates options -When `MakePasskeyCreationOptionsAsync` executes, it uses the app's `IdentityPasskeyOptions` configuration to determine the specific parameters for credential creation. These options control various aspects of the passkey creation process. +When executes, it uses the app's configuration to determine the specific parameters for credential creation. These options control various aspects of the passkey creation process. You can customize these options during application startup. For example: @@ -327,12 +310,7 @@ builder.Services.Configure(options => }); ``` -The `UserVerificationRequirement` option determines whether the authenticator must verify the user's identity (through biometric or PIN methods), while `ResidentKeyRequirement` indicates whether the credential should be discoverable, allowing authentication without first providing a username. For more information during the .NET 10 preview release period, see the [`IdentityPasskeyOptions` reference source (`dotnet/aspnetcore` GitHub repository)](https://github.com/dotnet/aspnetcore/blob/main/src/Identity/Core/src/IdentityPasskeyOptions.cs). - - +The option determines whether the authenticator must verify the user's identity (through biometric or PIN methods), while indicates whether the credential should be discoverable, allowing authentication without first providing a username. For more information, see . ### Step 4: Client requests credential @@ -387,16 +365,16 @@ In the Blazor Web App template, the returned credential is automatically seriali When the server receives the credential, it must verify its validity and store the public key for future authentication. This is where ASP.NET Core Identity's passkey APIs become crucial. -The `PerformPasskeyAttestationAsync` method validates the attestation response from the client. This comprehensive validation process: +The method validates the attestation response from the client. This comprehensive validation process: * Verifies that the credential type matches expectations. * Validates the client data JSON including origin and challenge. * Checks authenticator data flags for user presence and verification * Extracts and validates the public key. -If all checks pass, the method returns a `PasskeyAttestationResult` containing the verified passkey information. +If all checks pass, the method returns a containing the verified passkey information. -After the attestation is verified, the app uses `AddOrUpdatePasskeyAsync` to store the passkey in the database: +After the attestation is verified, the app uses to store the passkey in the database: ```csharp var attestationResult = @@ -416,11 +394,11 @@ if (!addResult.Succeeded) } ``` -The stored `UserPasskeyInfo` contains all of the necessary information for future authentication, including the credential ID, public key, signature counter for replay protection, and flags indicating whether the passkey is backed up or eligible for backup. +The stored contains all of the necessary information for future authentication, including the credential ID, public key, signature counter for replay protection, and flags indicating whether the passkey is backed up or eligible for backup. ### Step 8: Post-registration tasks -After successfully registering a passkey, apps often perform additional tasks to improve the user experience. A common pattern is to prompt users to provide a friendly name for their passkey, making it easier to identify among multiple credentials. The `UserPasskeyInfo.Name` property stores this user-friendly name, which can be updated using the same `AddOrUpdatePasskeyAsync` method: +After successfully registering a passkey, apps often perform additional tasks to improve the user experience. A common pattern is to prompt users to provide a friendly name for their passkey, making it easier to identify among multiple credentials. The property stores this user-friendly name, which can be updated using the same `AddOrUpdatePasskeyAsync` method: ```csharp passkey.Name = "My iPhone"; @@ -475,7 +453,7 @@ async function requestCredential(email, mediation, headers, signal) { } ``` -The `MakePasskeyRequestOptionsAsync` method generates these options. When you provide a specific user, it includes only that user's credentials in the allow list. When called without a user, it generates options suitable for conditional UI or username-less authentication: +The method generates these options. When you provide a specific user, it includes only that user's credentials in the allow list. When called without a user, it generates options suitable for conditional UI or username-less authentication: ```csharp app.MapPost("/Account/PasskeyRequestOptions", async ( @@ -494,7 +472,7 @@ app.MapPost("/Account/PasskeyRequestOptions", async ( ### Step 3: Server generates options -The server generates authentication options using the same `IdentityPasskeyOptions` configuration used during registration. The `ServerDomain` must match the domain where the passkey was originally registered, or authentication fails. The `UserVerificationRequirement` determines whether the authenticator must verify the user's identity during authentication. +The server generates authentication options using the same configuration used during registration. The must match the domain where the passkey was originally registered, or authentication fails. The determines whether the authenticator must verify the user's identity during authentication. ### Step 4: Client requests assertion @@ -547,7 +525,7 @@ The submission mechanism varies by app but typically involves either a form subm ### Step 7: Server verification -The server verifies the assertion to authenticate the user. ASP.NET Core Identity provides the `PasskeySignInAsync` method, which performs the complete authentication flow in a single call: +The server verifies the assertion to authenticate the user. ASP.NET Core Identity provides the method, which performs the complete authentication flow in a single call: ```csharp var result = await signInManager.PasskeySignInAsync(credentialJson); @@ -560,7 +538,7 @@ if (result.Succeeded) return Results.Unauthorized(); ``` -The `PasskeySignInAsync` method internally calls `PerformPasskeyAssertionAsync` to: +The method internally calls to: * Validate the assertion signature using the stored public key. * Verify that the challenge matches the one originally sent. @@ -569,14 +547,14 @@ The `PasskeySignInAsync` method internally calls `PerformPasskeyAssertionAsync` If all checks pass, the method signs in the user and returns a `SignInResult` indicating success. -For scenarios requiring more control, you can use `PerformPasskeyAssertionAsync` directly to validate the assertion without immediately signing in the user: +For scenarios requiring more control, you can use directly to validate the assertion without immediately signing in the user: -* `PerformPasskeyAssertionAsync` returns a `PasskeyAssertionResult` containing the authenticated user and updated passkey information. -* Because the passkey's sign-in count and authenticator flags may have changed since the last assertion and the updated passkey isn't automatically stored when calling `PerformPasskeyAssertionAsync`, call `userManager.AddOrUpdatePasskeyAsync` with the returned `PasskeyAssertionResult`. +* returns a containing the authenticated user and updated passkey information. +* Because the passkey's sign-in count and authenticator flags may have changed since the last assertion and the updated passkey isn't automatically stored when calling , call with the returned . ### Step 8: Session establishment -Upon successful authentication, ASP.NET Core Identity establishes an authenticated session for the user. The `PasskeySignInAsync` method handles this automatically, creating the necessary authentication cookies and claims. The app then redirects the user to protected resources or display personalized content. +Upon successful authentication, ASP.NET Core Identity establishes an authenticated session for the user. The method handles this automatically, creating the necessary authentication cookies and claims. The app then redirects the user to protected resources or display personalized content. ## Mitigate `PublicKeyCredential.toJSON` error (`TypeError: Illegal invocation`) From 1c0e912e8a62b4267a01c68e321f5e2579e0baa3 Mon Sep 17 00:00:00 2001 From: guardrex <1622880+guardrex@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:49:53 -0500 Subject: [PATCH 2/3] Updates --- aspnetcore/security/authentication/passkeys/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aspnetcore/security/authentication/passkeys/index.md b/aspnetcore/security/authentication/passkeys/index.md index 838eb0b5e962..a25c7c4fa580 100644 --- a/aspnetcore/security/authentication/passkeys/index.md +++ b/aspnetcore/security/authentication/passkeys/index.md @@ -167,7 +167,7 @@ builder.Services.Configure(options => }); ``` -For a complete list of configuration options, see . For the most up-to-date browser defaults, see the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/). +For a complete list of configuration options, see . For the most up-to-date browser defaults, see the [W3C WebAuthn specification](https://www.w3.org/TR/webauthn-3/). > [!NOTE] > Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next preview release of .NET. To select a tag for a specific release, use the **Switch branches or tags** dropdown list. For more information, see [How to select a version tag of ASP.NET Core source code (`dotnet/AspNetCore.Docs` #26205)](https://github.com/dotnet/AspNetCore.Docs/discussions/26205). @@ -310,7 +310,7 @@ builder.Services.Configure(options => }); ``` -The option determines whether the authenticator must verify the user's identity (through biometric or PIN methods), while indicates whether the credential should be discoverable, allowing authentication without first providing a username. For more information, see . +The option determines whether the authenticator must verify the user's identity (through biometric or PIN methods), while indicates whether the credential should be discoverable, allowing authentication without first providing a username. For more information, see . ### Step 4: Client requests credential From 5ab08c56c8a341ecebdc9062437b7fe83decbdee Mon Sep 17 00:00:00 2001 From: Luke Latham <1622880+guardrex@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:30:49 -0500 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- aspnetcore/blazor/call-web-api.md | 2 +- aspnetcore/blazor/state-management/server.md | 2 +- aspnetcore/security/authentication/passkeys/index.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aspnetcore/blazor/call-web-api.md b/aspnetcore/blazor/call-web-api.md index 3aa3342a7fe9..3ecb098a60ca 100644 --- a/aspnetcore/blazor/call-web-api.md +++ b/aspnetcore/blazor/call-web-api.md @@ -922,7 +922,7 @@ Follow the guidance in the article to add a PATCH contr :::moniker range=">= aspnetcore-10.0" -Add a package reference for the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson) NuGet package to the web API app. +Add a package reference for the [`Microsoft.AspNetCore.JsonPatch.SystemTextJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch.SystemTextJson) NuGet package to the web API app. In the `Program` file add an `@using` directive for the namespace: diff --git a/aspnetcore/blazor/state-management/server.md b/aspnetcore/blazor/state-management/server.md index c2d837d8a47f..9852e3a7753f 100644 --- a/aspnetcore/blazor/state-management/server.md +++ b/aspnetcore/blazor/state-management/server.md @@ -99,7 +99,7 @@ Persisting component state across circuits is built on top of the existing [NOTE] > Persisting component state for prerendering works for any interactive render mode, but circuit state persistence only works for the **Interactive Server** render mode. -Annotate component properties the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) to enable circuit state persistence. The following example also keys the items with the [`@key` directive attribute](xref:blazor/components/key) to provide a unique identifier for each component instance: +Annotate component properties with the [`[PersistentState]` attribute](xref:Microsoft.AspNetCore.Components.PersistentStateAttribute) to enable circuit state persistence. The following example also keys the items with the [`@key` directive attribute](xref:blazor/components/key) to provide a unique identifier for each component instance: ```razor @foreach (var item in Items) diff --git a/aspnetcore/security/authentication/passkeys/index.md b/aspnetcore/security/authentication/passkeys/index.md index a25c7c4fa580..a786afb6dac9 100644 --- a/aspnetcore/security/authentication/passkeys/index.md +++ b/aspnetcore/security/authentication/passkeys/index.md @@ -398,7 +398,7 @@ The stored contains all of ### Step 8: Post-registration tasks -After successfully registering a passkey, apps often perform additional tasks to improve the user experience. A common pattern is to prompt users to provide a friendly name for their passkey, making it easier to identify among multiple credentials. The property stores this user-friendly name, which can be updated using the same `AddOrUpdatePasskeyAsync` method: +After successfully registering a passkey, apps often perform additional tasks to improve the user experience. A common pattern is to prompt users to provide a friendly name for their passkey, making it easier to identify among multiple credentials. The property stores this user-friendly name, which can be updated using the same method: ```csharp passkey.Name = "My iPhone"; @@ -554,7 +554,7 @@ For scenarios requiring more control, you can use method handles this automatically, creating the necessary authentication cookies and claims. The app then redirects the user to protected resources or display personalized content. +Upon successful authentication, ASP.NET Core Identity establishes an authenticated session for the user. The method handles this automatically, creating the necessary authentication cookies and claims. The app then redirects the user to protected resources or displays personalized content. ## Mitigate `PublicKeyCredential.toJSON` error (`TypeError: Illegal invocation`)