Changelog
Modules

Passkeys

WebAuthn passwordless register/login and credential management endpoints.

Passkey (WebAuthn) endpoints for passwordless register/login and managing credentials on an existing account.

DI

builder.Services.AddPasskeyEndpoints<AppUser>();

This registers passkey rate limits/ReAuth and the default IdentityPasskeySignInCompleter<AppUser>. Prefer the generic overload for compose hosts so register/login can resolve IPasskeySignInCompleter<TUser>.

Facade: passkeys are enabled by default; set Passkeys.ServerDomain (required in Production).

Map

app.MapGroup("/account").MapPasskeyEndpoints<AppUser>();

Routes are mapped under {prefix}/passkeys (facade default /account/passkeys).

Routes

MethodPathAuth / extras
POST/passkeys/creationOptionsAuth + ReAuth + CSRF + rate limit
POST/passkeys/requestOptionsCSRF + rate limit; optional ?username=
POST/passkeys/register/optionsCSRF; body { email }
POST/passkeys/registerCSRF; passwordless create + completer sign-in
POST/passkeys/loginCSRF; completer sign-in (useCookies / useSessionCookies for Identity completer)
POST/passkeys/Add passkey (auth + ReAuth + CSRF)
GET/passkeys/List passkeys (auth)
PATCH/passkeys/Rename (auth + ReAuth + CSRF)
DELETE/passkeys/{credentialIdUrl}Remove (auth + ReAuth + CSRF)

Passwordless register flow

  1. POST /account/passkeys/register/options with { "email": "..." }
  2. Browser navigator.credentials.create(...)
  3. POST /account/passkeys/register with { "email": "...", "credentialJson": "..." }

Sign-in completer

After a successful register/login ceremony, AuthEndpoints calls IPasskeySignInCompleter<TUser>.

Default: Identity

IdentityPasskeySignInCompleter honors the same query flags as Identity login:

  • ?useCookies=true → persistent application cookie
  • ?useSessionCookies=true → session application cookie
  • neither → Identity bearer token (AccessTokenResponse)

Register also returns PasskeyCredentialResponse (credential id) after Identity sign-in.

Simple JWT

Register JwtPasskeySignInCompleter instead (requires AddJwtEndpoints):

builder.Services.AddPasskeyEndpoints<AppUser>();
builder.Services.AddJwtEndpoints<AppUser, AppDbContext>(o =>
{
    o.Issuer = "https://example.com";
    o.Audience = "https://example.com";
    o.SigningOptions.SymmetricKey = builder.Configuration["Jwt:SymmetricKey"];
});
builder.Services.AddPasskeySignInCompleter<AppUser, JwtPasskeySignInCompleter<AppUser>>();

Successful passkey register/login then returns the same shape as JWT /create: access token in JSON + HttpOnly refresh cookie. Cookie query flags are ignored for this completer.

Facade JWT opt-in does not auto-select the JWT completer — register it explicitly when you want Simple JWT after passkeys.

Constraints

  • Passwordless register needs IdentityUser with string or Guid key.
  • CSRF is required for WebAuthn ceremonies.
  • Sensitive credential mutations require ReAuth.