For registration: generate a PublicKeyCredentialCreationOptions object server-side including a cryptographically random challenge, relying party info (id and name), user info (id, name, displayName), and pubKeyCredParams listing acceptable algorithms.
Send the options to the client; the browser calls navigator.credentials.create() and returns an AuthenticatorAttestationResponse; POST the credential id, attestation object, and client data JSON back to your server.
Verify the registration server-side: parse and validate the clientDataJSON (check type, challenge, and origin), verify the attestation object, extract the public key and credential id, and store them associated with the user.
For authentication: generate a new random challenge and a PublicKeyCredentialRequestOptions including allowCredentials listing the user's stored credential ids; send to the client.
The client calls navigator.credentials.get(); the authenticator signs the challenge and returns an AuthenticatorAssertionResponse; POST it to your server.
Verify the assertion server-side: validate clientDataJSON, verify the authenticatorData flags (user presence, user verification as required), verify the signature against the stored public key, and increment the stored signature counter to detect cloned authenticators.
Known gotchas
The rpId must be a registrable domain suffix of the page origin; mismatches (including scheme or port differences) cause the ceremony to fail with a security error and are one of the most common passkey integration mistakes.
Challenges must be single-use and expire; reusing or not expiring server-side challenges opens replay attack vectors — store them with a short TTL tied to the session.
A signature counter that decreases or stays the same between authentications is a signal of a cloned authenticator; handle this according to your security policy (at minimum log it; consider rejecting the authentication).
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