Most applications still ask users to enter an email address and password to sign in. This approach works, but users can forget passwords, reuse them across websites, or fall for fake login pages.
Passkeys offer a different way to sign in. Instead of entering a password, users can verify their identity using a fingerprint, face recognition, a device PIN, or a supported security key.
ASP.NET Core Identity includes built-in passkey support, making it easier to add passwordless authentication without building the entire WebAuthn authentication process yourself.
In this article, we will look at how passkeys work, how to configure them in ASP.NET Core, and what to consider before using them in a real application.
What Is a Passkey?
A passkey is a digital credential that allows a user to sign in without entering a traditional password.
When a user creates a passkey, their device generates a public-private key pair. The application stores the public key, while the private key stays protected by the user's device or passkey provider.
During login, the server sends a challenge to the browser. The user's authenticator signs that challenge with the private key, and the server verifies the response using the stored public key.
The private key is never sent to the application server.
Passkeys use the Web Authentication API, commonly called WebAuthn, together with public-key cryptography.
Passkeys vs. Passwords
Feature | Password login | Passkey login |
|---|
User action | Enter a password | Approve a device authentication request |
Server storage | Password hash and related account data | Public-key credential information |
Phishing resistance | Depends on additional protections | Strong protection through domain-bound credentials |
Password resets | Usually required when passwords are forgotten | Not required for the passkey itself |
Device support | Works through supported login forms | Requires a compatible browser and authenticator |
Account recovery | Password reset process | Recovery or another sign-in method is still needed |
Passkeys do not remove every authentication risk. Account recovery, device security, session management, and authorization still need careful handling.
Step 1: Check the Requirements
The built-in passkey APIs in ASP.NET Core Identity are available in .NET 10 and later.
For this implementation, you need:
The .NET 10 SDK or a later compatible SDK.
An ASP.NET Core application configured with ASP.NET Core Identity.
A user store that supports Identity's passkey operations.
A modern browser that supports WebAuthn.
A compatible device, security key, or passkey provider.
Passkey operations require a secure context. Use HTTPS in production. Local development can use a browser's supported localhost exception.
If you already have an ASP.NET Core Identity application, you do not need to replace your existing user system. You can add passkeys as another sign-in method.
Step 2: Configure Passkey Options
ASP.NET Core Identity provides IdentityPasskeyOptions to configure passkey behavior.
Add the following configuration to Program.cs:
using Microsoft.AspNetCore.Identity;
var builder = WebApplication.CreateBuilder(args);
// Register your existing Identity services here.
builder.Services.Configure<IdentityPasskeyOptions>(options =>
{
options.ServerDomain = "example.com";
options.AuthenticatorTimeout = TimeSpan.FromMinutes(3);
options.UserVerificationRequirement = "required";
});
Replace example.com with your application's actual relying party domain.
The important settings are:
ServerDomain identifies the domain associated with the passkey. It must match the domain used during registration.
AuthenticatorTimeout tells the browser how long the authenticator should wait. The browser may treat this value as a hint.
UserVerificationRequirement controls whether the authenticator must verify the user, such as through a device PIN or biometric check.
Do not copy the example domain into a deployed application. Choose the correct domain for your environment and configure host-header validation appropriately.
Your existing Identity setup must also include its normal authentication, user-store, and cookie configuration. The example above only shows the additional passkey settings.
Step 3: Register a Passkey for a User
A user generally adds a passkey from an account security page after signing in.
The registration process follows these steps:
The user selects Add passkey.
The server generates registration options and a one-time challenge.
The browser passes those options to the device authenticator.
The user approves the request using the available verification method.
The browser sends the resulting credential to the server.
ASP.NET Core Identity validates the response and stores the passkey information.
The server does not simply accept whatever public key the browser sends. It must validate the challenge, origin, credential data, and other required information.
ASP.NET Core Identity provides SignInManager<TUser> methods for this workflow.
Generate Registration Options
The following example shows how an authenticated user can request registration options:
app.MapPost("/account/passkey/creation-options",
async (
HttpContext context,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager) =>
{
var user = await userManager.GetUserAsync(context.User);
if (user is null)
{
return Results.Unauthorized();
}
var userId = await userManager.GetUserIdAsync(user);
var userName = await userManager.GetUserNameAsync(user)
?? "User";
var optionsJson =
await signInManager.MakePasskeyCreationOptionsAsync(
new PasskeyUserEntity
{
Id = userId,
Name = userName,
DisplayName = userName
});
return Results.Content(
optionsJson,
contentType: "application/json");
})
.RequireAuthorization();
This example assumes ApplicationUser is your Identity user type and the Identity services are already configured.
The endpoint requires authentication because the passkey is being added to an existing account. It gets the user from the current authenticated session rather than trusting a user ID supplied by the browser.
ASP.NET Core Identity also manages temporary registration state for this workflow. Use the SignInManager APIs instead of creating your own challenge and state-handling mechanism.
The next step is to connect this endpoint to browser-side JavaScript using the WebAuthn API. The browser uses navigator.credentials.create() to request a new credential from the authenticator.
After the browser returns the credential, the application must submit it to the server for verification and storage. The endpoint shown above only generates registration options; it does not complete the registration by itself.
Step 4: Implement Passkey Login
Once a passkey has been registered, the user can use it to sign in.
The login flow is similar to registration, but the browser requests an existing credential instead of creating a new one.
Generate Authentication Options
Create an endpoint that generates the options for a passkey login:
app.MapPost("/account/passkey/request-options",
async (
string? username,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager) =>
{
var user = string.IsNullOrWhiteSpace(username)
? null
: await userManager.FindByNameAsync(username);
var optionsJson =
await signInManager.MakePasskeyRequestOptionsAsync(user);
return Results.Content(
optionsJson,
contentType: "application/json");
});
When a username is supplied, the server can generate options for that account. When no user is supplied, the options can support discoverable credentials, depending on the application and its configuration.
In a production implementation, make sure this endpoint follows your account-enumeration and rate-limiting policies. Avoid revealing whether a particular email address has an account or a registered passkey.
The browser then uses navigator.credentials.get() to request a signed response from the authenticator.
Verify the Login Response
After the browser submits the credential response, ASP.NET Core Identity can validate it and sign in the user.
app.MapPost("/account/passkey/sign-in",
async (
PasskeyLoginRequest request,
SignInManager<ApplicationUser> signInManager) =>
{
var result =
await signInManager.PasskeySignInAsync(
request.CredentialJson);
if (!result.Succeeded)
{
return Results.Unauthorized();
}
return Results.Ok(new
{
Message = "Signed in successfully."
});
});
public sealed record PasskeyLoginRequest(
string CredentialJson);
This endpoint assumes that the application has already requested authentication options through MakePasskeyRequestOptionsAsync for the current login attempt.
The browser must serialize the WebAuthn credential into the expected JSON format and send it in CredentialJson.
The PasskeySignInAsync method validates the response against the stored credential and the challenge associated with the login attempt. If validation succeeds, Identity establishes the authenticated session using its configured authentication scheme.
For a cookie-based application, the browser must receive and retain the authentication cookie. For an API using a different authentication scheme, adapt the session-establishment process to that scheme.
The endpoint is an illustration of the server-side verification step, not a complete login page. You still need browser-side JavaScript, suitable request handling, anti-forgery protection where applicable, and proper cookie configuration.
Step 5: Handle Recovery and Passkey Management
Users can lose devices, replace phones, or lose access to a passkey provider. A passkey-only login system must account for these situations.
Consider implementing the following features:
Allow users to register more than one passkey.
Provide a secure account-recovery process.
Let users review and remove registered passkeys.
Ask users to verify their identity before adding or removing authentication methods.
Notify users about important account-security changes.
If a user is already signed in, do not allow an unverified request to attach a new passkey to their account.
Also, limit the number of passkeys a user can register and validate display-name lengths. These controls help prevent unnecessary database growth and abuse.
Common Mistakes to Avoid
Assuming a Fingerprint Is Sent to the Server
The application normally receives a cryptographic credential response, not the user's fingerprint or face data. The authenticator handles local user verification.
Skipping Challenge Validation
The server must verify that the response belongs to the current authentication attempt. Never accept a credential simply because it contains a valid-looking public key or credential ID.
Ignoring Domain Configuration
Passkeys are associated with a relying party domain. Incorrect domain settings can cause registration or login to fail. Validate host headers and configure ServerDomain explicitly when strict domain control is required.
Building a Custom WebAuthn Implementation Without a Need
Implementing challenge generation, signature verification, origin validation, and credential storage yourself introduces avoidable security risks. Prefer the built-in Identity APIs when they meet your requirements.
Forgetting Account Recovery
Passwordless login still needs a recovery plan. Without one, users may lose access to their accounts when their available authenticators become unavailable.
Best Practices for Production Applications
Use HTTPS everywhere. Passkey operations should run over a secure connection.
Protect registration endpoints. Require authentication when adding a passkey to an existing account.
Use the built-in Identity workflow. Let SignInManager handle the protected state required between registration and login requests.
Validate your deployment domain. Configure the relying party domain and trusted hosts correctly.
Keep error messages safe. Avoid exposing sensitive account details in failed login responses.
Add rate limiting and monitoring. Watch for repeated failed attempts and unusual registration activity.
Test on supported devices and browsers. Passkey support and user experience can vary by platform.
Keep a recovery option. Decide how users will regain access if their passkeys become unavailable.
Advantages and Disadvantages of Passkeys
Advantages
Users do not need to remember or type a password.
Passkeys offer strong protection against phishing because credentials are bound to the relying party.
The server stores public-key credential information instead of a reusable login password.
ASP.NET Core Identity provides built-in APIs for common passkey registration and login flows.
Disadvantages
Browser and authenticator support is required.
Registration and login involve browser-side JavaScript as well as server-side code.
Account recovery requires additional planning.
Domain, origin, session, and credential management must be configured correctly.
Built-in Identity passkey support is designed for common authentication scenarios rather than every possible WebAuthn use case.
Summary
Passkeys provide a practical way to add passwordless authentication to ASP.NET Core applications. They let users sign in through a supported authenticator while the server verifies a cryptographic response instead of checking a password.
With .NET 10 or later, ASP.NET Core Identity provides built-in passkey APIs for generating registration and login options, validating credentials, and establishing authenticated sessions.
The main work is connecting these APIs to your login interface and building a secure registration, recovery, and credential-management experience. Start with the built-in Identity workflow, configure the correct domain, require HTTPS, and test the complete process before enabling passkeys for production users.
Join the conversation! Your thoughts help the community grow.