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.