On the server, generate a registration options object containing a random cryptographic challenge (at least 16 bytes), the relying party (rp) name and id, the user id and display name, and the pubKeyCredParams list specifying supported algorithms (e.g., ES256, RS256).
Serialize the options and send them to the browser; call navigator.credentials.create({ publicKey: options }) after decoding the challenge and user id from base64url.
The browser prompts the user to create a passkey using a platform authenticator or roaming key; on success, the API returns a PublicKeyCredential containing the attestation object and client data JSON.
Send the credential response to the server; verify the client data JSON (check type is 'webauthn.create', origin matches your domain, and challenge matches), then parse and validate the attestation object.
Extract the public key and credential ID from the authenticator data inside the attestation object; store both securely associated with the user's account.
Respond with success to the client; the passkey is now registered and can be used for future authentication ceremonies.
Known gotchas
The challenge must be freshly generated per registration attempt and verified server-side; reusing challenges opens replay attack vectors.
The relying party id (rp.id) must be equal to or a registrable domain suffix of the page origin; mismatches cause the ceremony to fail silently in some browsers.
Use a well-audited WebAuthn server library rather than implementing attestation parsing from scratch; the binary encoding (CBOR) and cryptographic validation are error-prone to hand-roll.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp