diff --git a/aspnetcore/blazor/components/sections.md b/aspnetcore/blazor/components/sections.md index 9b55207c7b1d..719b36561b21 100644 --- a/aspnetcore/blazor/components/sections.md +++ b/aspnetcore/blazor/components/sections.md @@ -5,7 +5,7 @@ description: Learn how to control the content in a Razor component from a child monikerRange: '>= aspnetcore-8.0' ms.author: wpickett ms.custom: mvc -ms.date: 11/12/2024 +ms.date: 09/10/2025 uid: blazor/components/sections --- # ASP.NET Core Blazor sections @@ -85,6 +85,10 @@ When the `Counter` component is accessed, the `MainLayout` component renders the > [!NOTE] > and components can only set either or , not both. +## `RenderFragment` caching rules and section rendering behavior + +When a 's content changes, which is a different instance than the component where it's rendered, Blazor completely destroys and recreates the section instead of attempting to update the section's content. Unlike normal rendering, the section's content could come from different instances, and it doesn't make sense to attempt processing content from two separate components, which might lead to unexpected results. For a detailed explanation on this behavior, see [Inconsistent component initialization with Blazor SectionOutlet/SectionContent and CascadingValue (`dotnet/aspnetcore` #58316)](https://github.com/dotnet/aspnetcore/issues/58316). + ## Section interaction with other Blazor features A section interacts with other Blazor features in the following ways: diff --git a/aspnetcore/security/authentication/passkeys/blazor.md b/aspnetcore/security/authentication/passkeys/blazor.md index 32a0f164d842..686f3c699273 100644 --- a/aspnetcore/security/authentication/passkeys/blazor.md +++ b/aspnetcore/security/authentication/passkeys/blazor.md @@ -3,8 +3,9 @@ title: Implement passkeys in ASP.NET Core Blazor Web Apps author: guardrex description: Learn how to implement passkeys authentication in ASP.NET Core Blazor Web Apps. ms.author: wpickett +monikerRange: '>= aspnetcore-10.0' ms.custom: mvc -ms.date: 09/08/2025 +ms.date: 09/10/2025 uid: security/authentication/passkeys/blazor zone_pivot_groups: implementation --- @@ -237,23 +238,26 @@ Add the following `RenamePasskey` component for renaming passkeys and update the Add a link to the passkey management page in the app's `ManageNavMenu` component. -In `Components/Account/Shared/ManageNavMenu.razor`: +In `Components/Account/Shared/ManageNavMenu.razor`, add the following [`NavLink` component](xref:blazor/fundamentals/routing#navlink-component) for the `Passkeys` component: -```diff -+ +```razor + ``` ## Include the JavaScript file -In the `App` component, add a reference to the `PasskeySubmit` JavaScript file after the Blazor script. +In the `App` component (`Components/App.razor`), locate the [Blazor script](xref:blazor/project-structure#location-of-the-blazor-script) tag: -In `Components/App.razor`: - -```diff +```razor -+ +``` + +Immediately after the Blazor script tag, add a reference to the `PasskeySubmit` JavaScript module: + +```razor + ``` :::zone-end @@ -279,6 +283,14 @@ After a passkey is registered: 1. Navigate to `Account/Manage/Passkeys` to add, rename, or delete passkeys. 1. If the passkey supports passkey autofill (conditional UI) for login, test the passkey autofill feature by selecting the email input field when you have saved passkeys. +## Mitigate `PublicKeyCredential.toJSON` error (`TypeError: Illegal invocation`) + +Some password managers don't implement the [`PublicKeyCredential.toJSON` method](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/toJSON) correctly, which is required for [`JSON.stringify`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) to work when serializing passkey credentials. When registering or authenticating a user with an app based on the Blazor Web App project template, the following error is thrown when attempting to add a passkey: + +> :::no-loc text="Error: Could not add a passkey: Illegal invocation"::: + +For guidance on mitigating this error, see . + ## Additional resources * [Web Authentication API (MDN documentation)](https://developer.mozilla.org/docs/Web/API/Web_Authentication_API) diff --git a/aspnetcore/security/authentication/passkeys/index.md b/aspnetcore/security/authentication/passkeys/index.md index 617163a86fb1..48207802dba7 100644 --- a/aspnetcore/security/authentication/passkeys/index.md +++ b/aspnetcore/security/authentication/passkeys/index.md @@ -4,7 +4,8 @@ author: guardrex description: Discover how to enable Web Authentication API (WebAuthn) passkeys in ASP.NET Core apps. ms.author: wpickett monikerRange: '>= aspnetcore-10.0' -ms.date: 09/08/2025 +ms.custom: mvc +ms.date: 09/10/2025 uid: security/authentication/passkeys/index --- # Enable Web Authentication API (WebAuthn) passkeys @@ -573,6 +574,93 @@ For scenarios requiring more control, you can use `PerformPasskeyAssertionAsync` 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. +## Mitigate `PublicKeyCredential.toJSON` error (`TypeError: Illegal invocation`) + +The [`PublicKeyCredential.toJSON` method](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential/toJSON) returns a JSON representation of a [`PublicKeyCredential`](https://developer.mozilla.org/docs/Web/API/PublicKeyCredential). The method is invoked by the password manager when the app attempts to serialize a `PublicKeyCredential` by calling [`JSON.stringify`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) while registering or authenticating a user. + +Some password managers don't implement the `PublicKeyCredential.toJSON` method correctly, which is required for `JSON.stringify` to work when serializing passkey credentials. When registering or authenticating a user with an app based on the Blazor Web App project template, the following error is thrown by some password managers when attempting to add a passkey: + +> :::no-loc text="Error: Could not add a passkey: Illegal invocation"::: + +Until your selected password manager is updated to implement the `PublicKeyCredential.toJSON` method correctly, make the following changes to the app. The following code manually JSON serializes the `PublicKeyCredential`. + +In the `Components/Account/Shared/PasskeySubmit.razor.js` file, locate the `passkey-submit` custom element definition code block: + +```javascript +customElements.define('passkey-submit', class extends HTMLElement { + ... +}); +``` + +Add the following `convertToBase64` function to the code block: + +```javascript +convertToBase64(o) { + if (!o) { + return undefined; + } + + // Normalize Array to Uint8Array + if (Array.isArray(o)) { + o = Uint8Array.from(o); + } + + // Normalize ArrayBuffer to Uint8Array + if (o instanceof ArrayBuffer) { + o = new Uint8Array(o); + } + + // Convert Uint8Array to base64 + if (o instanceof Uint8Array) { + let str = ''; + for (let i = 0; i < o.byteLength; i++) { + str += String.fromCharCode(o[i]); + } + o = window.btoa(str); + } + + if (typeof o !== 'string') { + throw new Error("Could not convert to base64 string"); + } + + // Convert base64 to base64url + o = o.replace(/\+/g, "-").replace(/\//g, "_").replace(/=*$/g, ""); + + return o; +} +``` + +In the `obtainAndSubmitCredential` function of the code block, locate the line that calls `JSON.stringify` with the user's credential and remove the line: + +```diff +- const credentialJson = JSON.stringify(credential); +``` + +Replace the preceding line with the following code: + +```javascript +const credentialJson = JSON.stringify({ + authenticatorAttachment: credential.authenticatorAttachment, + clientExtensionResults: credential.getClientExtensionResults(), + id: credential.id, + rawId: this.convertToBase64(credential.rawId), + response: { + attestationObject: this.convertToBase64(credential.response.attestationObject), + authenticatorData: this.convertToBase64(credential.response.authenticatorData ?? + credential.response.getAuthenticatorData?.() ?? undefined), + clientDataJSON: this.convertToBase64(credential.response.clientDataJSON), + publicKey: this.convertToBase64(credential.response.getPublicKey?.() ?? undefined), + publicKeyAlgorithm: credential.response.getPublicKeyAlgorithm?.() ?? undefined, + transports: credential.response.getTransports?.() ?? undefined, + signature: this.convertToBase64(credential.response.signature), + userHandle: this.convertToBase64(credential.response.userHandle), + }, + type: credential.type, +}); +``` + +The preceding workaround is only required until the password manager is updated to implement the `PublicKeyCredential.toJSON` method correctly. We recommend tracking your password manager's release notes and reverting the preceding changes after the password manager is updated. + ## Additional resources * [Web Authentication API (MDN documentation)](https://developer.mozilla.org/docs/Web/API/Web_Authentication_API)