From c9ffcd20e2895b7be08884ed2b782bcedc13274d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20V=C4=B1=CC=81zner?= Date: Mon, 31 Aug 2026 15:17:20 +0200 Subject: [PATCH] Fix outdated template references in the passkeys articles --- .../authentication/passkeys/blazor.md | 29 ++-- .../security/authentication/passkeys/index.md | 148 +++++++++++++++++- 2 files changed, 159 insertions(+), 18 deletions(-) diff --git a/aspnetcore/security/authentication/passkeys/blazor.md b/aspnetcore/security/authentication/passkeys/blazor.md index 383e7c3a8dba..ba0c668065d4 100644 --- a/aspnetcore/security/authentication/passkeys/blazor.md +++ b/aspnetcore/security/authentication/passkeys/blazor.md @@ -1,10 +1,11 @@ --- title: Implement passkeys in ASP.NET Core Blazor Web Apps +ai-usage: ai-assisted author: guardrex description: Learn how to implement passkeys authentication in ASP.NET Core Blazor Web Apps. ms.author: wpickett monikerRange: '>= aspnetcore-10.0' -ms.date: 10/30/2025 +ms.date: 08/31/2026 uid: security/authentication/passkeys/blazor zone_pivot_groups: implementation --- @@ -191,6 +192,12 @@ Add the following model classes to the project in the `Components/Account` folde * [`Components/Account/PasskeyInputModel.cs`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/PasskeyInputModel.cs): Holds the JSON passkey credential for passkey sign-in operations (`Login` component) and adding passkeys (`Passkeys` component). * [`Components/Account/PasskeyOperation.cs`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/PasskeyOperation.cs): Defines the authentication action to be performed (`PassKeySubmit` component), either registering a new passkey (`Create`/0) or authenticating with an existing passkey (`Request`/1). +:::moniker range=">= aspnetcore-11.0" + +* [`Components/Account/PasskeyAuthenticators.cs`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/PasskeyAuthenticators.cs): Maps Authenticator Attestation GUIDs (AAGUIDs) to friendly display names, so a new passkey from a known authenticator is named automatically instead of prompting the user. + +:::moniker-end + ## Create the `PasskeySubmit` component Add the following `PasskeySubmit` component to handle passkey operations: @@ -207,7 +214,7 @@ Add the following JavaScript file to handle WebAuthn API interactions: Update the `IdentityComponentsEndpointRouteBuilderExtensions.cs` file (or create the file if it doesn't exist and call `MapAdditionalIdentityEndpoints` in the [`Program` file](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Program.cs#L129-L130)) to include the passkey-specific endpoints: -[`/PasskeyCreationOptions` and `/PasskeyRequestOptions` endpoints](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs#L53-L90) +[`/PasskeyCreationOptions` and `/PasskeyRequestOptions` endpoints](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs#L80-L132) ## Update the Login page @@ -215,18 +222,6 @@ Replace the existing `Login` component with the following component and update t [`Components/Account/Pages/Login.razor`](https://github.com/dotnet/aspnetcore/blob/main/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWebCSharp.1/Components/Account/Pages/Login.razor) -## Add a redirect method to the `IdentityRedirectManager` class - -Add the following method to the `IdentityRedirectManager` class in `Components/Account/IdentityRedirectManager.cs`: - -```csharp -public void RedirectToInvalidUser( - UserManager userManager, HttpContext context) => - RedirectToWithStatus("Account/InvalidUser", - $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", - context); -``` - ## Create passkey management pages for adding and renaming passkeys Add the following `Passkeys` component for managing passkeys and update the `BlazorWebCSharp._1.Data` namespace to match the app (for example: `Contoso.Components.Account.Data`): @@ -254,13 +249,13 @@ In `Components/Account/Shared/ManageNavMenu.razor`, add the following [`NavLink` In the `App` component (`Components/App.razor`), locate the [Blazor script](xref:blazor/project-structure#location-of-the-blazor-script) tag: ```razor - + ``` Immediately after the Blazor script tag, add a reference to the `PasskeySubmit` JavaScript module: ```razor - + ``` :::zone-end @@ -282,7 +277,7 @@ After a passkey is registered: 1. Sign out of the app. 1. On the login page, enter your email address. 1. Select **Log in with a passkey**. -4. Follow the browser's prompts to authenticate with your passkey. +1. Follow the browser's prompts to authenticate with your passkey. 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. diff --git a/aspnetcore/security/authentication/passkeys/index.md b/aspnetcore/security/authentication/passkeys/index.md index ada6a22590f8..0df5180b24c0 100644 --- a/aspnetcore/security/authentication/passkeys/index.md +++ b/aspnetcore/security/authentication/passkeys/index.md @@ -5,7 +5,7 @@ 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: 08/07/2026 +ms.date: 08/31/2026 uid: security/authentication/passkeys/index --- # Enable Web Authentication API (WebAuthn) passkeys @@ -453,6 +453,8 @@ After registration is initiated, the browser must obtain creation options from t From the browser's perspective, this step involves making an HTTP request to the server: +:::moniker range="< aspnetcore-11.0" + ```javascript async function createCredential(headers, signal) { // Step 2: Request creation options from the server @@ -469,6 +471,27 @@ async function createCredential(headers, signal) { } ``` +:::moniker-end + +:::moniker range=">= aspnetcore-11.0" + +```javascript +async function createCredential(signal) { + // Step 2: Request creation options from the server + const optionsResponse = + await fetchWithErrorHandling('/Account/PasskeyCreationOptions', + { + method: 'POST', + signal, + }); + const optionsJson = await optionsResponse.json(); + const options = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson); + return await navigator.credentials.create({ publicKey: options, signal }); +} +``` + +:::moniker-end + The application should define an endpoint that generates these options: ```csharp @@ -522,6 +545,8 @@ 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 @@ -684,6 +777,8 @@ The server generates authentication options using the same If all checks pass, the method signs in the user and returns a `SignInResult` indicating success. +:::moniker range=">= aspnetcore-11.0" + +The method distinguishes between a recoverable user action and an app error. If the passkey session state is missing or expired, for example because the user took too long to respond to the authenticator, the method returns . Calling the method without a preceding call to throws an , because that indicates a problem in the app rather than something the user can retry. + +:::moniker-end + For scenarios requiring more control, you can use directly to validate the assertion without immediately signing in the user: * returns a containing the authenticated user and updated passkey information.