The registration ceremony

Registering a passkey is a three-step round trip. The server hands the browser a random challenge and some rules about what kind of credential it'll accept. The browser passes that straight to navigator.credentials.create(), which is where your authenticator (Touch ID, Windows Hello, a security key, your phone) actually generates a new key pair and signs the challenge. The browser hands the result back to the server, which checks that signature before trusting the new public key.

1. The browser asks for options, then calls the real WebAuthn API

No @simplewebauthn/browser helper here — this is the actual navigator.credentials.create() call, using options fetched from the server a moment earlier.

const credential = (await navigator.credentials.create({
  publicKey,
})) as PublicKeyCredential;
const response = credential.response as AuthenticatorAttestationResponse;
// 

2. The server verifies the signed challenge

verifyRegistrationResponse() checks the challenge matches, the origin and RP ID are on the allowlist, and the attestation statement's signature is valid — before the new public key is ever stored.

const result = await verifyRegistrationResponse({
  response,
  expectedChallenge: challenge.challenge,
  expectedOrigin: rp.expectedOrigin,
  expectedRPID: rp.expectedRPID,
});
// 

Try it

This creates a demo account for your session if you don't have one yet, then walks through the ceremony above with your own authenticator. The Ceremony Inspector below shows exactly what got sent and what the server checked.

One subtlety worth knowing: the options above actually carry two separate identity fields — user.name (a stable identifier; this demo uses your account's original sci-fi handle and never changes it after creation) and user.displayName (a friendly, editable label — the Demo Lab's "Beam me a name" button changes this one). Your OS or password manager's passkey picker shows user.name, not displayName — so renaming your demo account won't change what it displays for any passkey, old or new. The Inspector below shows both values so you can see the difference directly.