Bind the attempt
Create an unpredictable attempt ID on your server and store it in the visitor’s server session. Keep it stable through retries. Give the collector that ID astag, or give the widget the ID as data-cdata with data-action="signup".
A collector request ID or ceremony ID received from the browser is not authorization by itself. Read the result with your secret key and compare its context with the attempt stored on your server.
Read the probe
uncontradicted means no conclusive rule found a contradiction. Read families[].coverage and sensor when your policy requires particular measurements. A missing or refused result does not satisfy a requirement for a completed probe.
Compare identifiers
All identity facets are
conditional. A match is useful evidence for your policy, not proof of one person or one physical device. Browser updates, privacy settings, timezone changes, and hardware changes can alter identifiers. Version 3 keeps portrait and landscape dimensions in the same order.
Compare probe identifiers within the same site key and identity.version. Compare ceremony identifiers within the same tenant, identity.scope, and version. Keep these two namespaces separate. For the rendering facet, also store and compare identity.rendering.version. Never fall back to an empty string when the facet is unavailable.
Your rule can reject a known instance, or a known machine and address pair, before minting a challenge. Identical devices sharing an address can also match.
Step up to a ceremony
Mint a challenge from your backend after evaluating the probe. Save the returnedchallenge_id on the pending attempt before sending its URL to the browser. Use a server-chosen account_ref when you intend to bind a passkey to an account.
When the browser says the ceremony finished, read the saved challenge ID, not an arbitrary ID from the request. Require status === "passed", compare account_ref when you supplied one, and require an unexpired completion for your action. A historical passed outcome remains readable; it is not a reusable signup permission.
In a desktop hand-off, save both devices.desktop and devices.ceremony. The phone completes the ritual. The rolling QR checks freshness of the hand-off; it does not establish that the devices share a room.
Mint a challenge and read its outcome describe the API.
Complete the write once
In one transaction in your application database:- Lock or conditionally claim the pending attempt for its server session.
- Recheck your account-duplication rule.
- Insert the probe or challenge ID into a used-proof table with a unique constraint.
- Create the account and store its measurement evidence.
- Mark the attempt completed with that account ID.
Widget path
The widget handles the probe and ceremony sequence. Your backend calls siteverify with the expected hostname, action, and attempt ID from its own session. Then perform the same transaction above.siteverify supplies request_id, challenge_id, and the ceremony’s devices when one ran.
Compare rendering after other inputs change
Store the rendering signature alongside the instance and machine identifiers in your account records. If the instance changes but the rendering signature matches, the browser reported the same stable rasters with different device or browser declarations. Your policy can require an existing-account login, a challenge, or a review. The match alone does not prove that the visitor owns another account. For a probe, index(site_id, rendering_version, rendering_hash). For a challenge
leg, index (tenant_id, identity_scope, rendering_version, rendering_hash) and keep
that table separate from probe identifiers. Save both legs of a desktop challenge.
A synchronized passkey or a shared phone is not a unique-person identifier either.