Skip to main content
Passkeys let users complete MFA with a biometric or device authenticator (Face ID, Touch ID, Windows Hello, or a hardware security key) instead of typing a code. Passkeys build on the WebAuthn standard, so the private key never leaves the user’s device.
Passkey MFA is supported in web environments only. TOTP and SMS remain available on all platforms. Enrollment and verification each run a single browser prompt—there is no code to enter.

How passkeys differ from TOTP and SMS

  • No code entry: enrollment and verification are single WebAuthn ceremonies driven by navigator.credentials, not a 6-digit code.
  • Single-call SDK surface: enrollPasskey and verifyPasskey run initiate → browser prompt → submit in one call, so they don’t use the shared initiate/submit MFA hooks.
  • Multiple credentials: a user can register more than one passkey (for example, one per device). Enrolled passkeys are a list, unlike the single TOTP or SMS registration.
  • User gesture required: verifyPasskey must be called from a user gesture (a click). Browsers block the credential prompt otherwise.

Project configuration

Enable passkey for your project in the CDP Portal. Passkey has two settings: the method toggle itself, and User verification. Unlike TOTP and SMS, the passkey toggle is enforced server-side. Enrollment fails while passkey is off for the project, even from the hooks or the Core SDK. Users who already enrolled can still verify with their passkeys if you later turn the method off.

Allowed origins

Every passkey ceremony is scoped to the origin it runs on:
  • Passkey requests must carry an Origin header, and that origin must be one of your project’s configured CORS origins. A request from an unlisted origin is rejected with Origin is not an allowed origin for this project.
  • The WebAuthn relying party ID is that origin’s exact host, not its registrable domain. An origin of https://app.example.com produces credentials scoped to app.example.com.
  • There is nothing to configure and nothing to prove by DNS. The relying party ID is derived per ceremony and recorded on each credential, where you can read it back as PasskeySummary.rpId.
Because the relying party ID is the exact host, a passkey enrolled on app.example.com cannot verify on wallet.example.com. A user who signs in on both hosts needs a passkey on each. Verification from a host with no matching credential fails the same way it would for a user with no passkey at all.

User verification

The optional userVerification setting controls whether the authenticator must verify the user (for example, with biometrics or a PIN). It appears in the Portal as a User verification select once passkey is on:
  • preferred (default): request user verification, but allow the ceremony to proceed without it.
  • required: fail the ceremony if the authenticator cannot verify the user.
  • discouraged: skip user verification where possible.

Enrollment

Passkey enrollment runs the full WebAuthn registration ceremony in a single call. Gate the UI on passkey support so it only appears where the browser supports WebAuthn.
useIsPasskeySupported is a query hook: it runs the check on mount and exposes the answer on data, which is undefined until the check resolves. enrollPasskey never rejects, so an event handler needs no catch; read the outcome from status and error.
Use enrollPasskeyAsync instead when you need the enrolled user inline. It resolves with { user } and rejects on failure.
The optional name is a human-friendly label (for example, the device or browser) surfaced when listing enrolled passkeys.

Verification

Passkey verification also runs in a single call. Call it from a user gesture.
Use verifyPasskeyAsync when the next step depends on the result. The non-throwing verifyPasskey resolves even when the ceremony fails, so retrying the original operation after it would retry on a cancelled prompt too.

Managing enrolled passkeys

A user may enroll up to 20 passkeys, so list and delete them by credential ID.
useListPasskeys fetches on mount and keys the query on the signed-in user, so enrolling or deleting a passkey does not refresh it. Call refetch after a mutation you want reflected on screen.
Deleting one of several passkeys always succeeds. Deleting the user’s only passkey is rejected when passkey is their last enrolled MFA method and your project requires MFA verification on login. Enroll another method first.
Enrollment past the 20-credential limit is rejected with A maximum of 20 passkeys may be enrolled. Delete one before enrolling another. Each PasskeySummary describes an enrolled passkey:

Checking passkey enrollment

Passkey is included in the standard MFA enrollment helpers.

Pre-built UI

The @coinbase/cdp-react EnrollMfa and VerifyMfa components render passkey alongside TOTP and SMS when the project has passkey enabled and the browser supports it. Passkey shows a single button (no code input); TOTP and SMS are unchanged. No extra wiring is required beyond the standard components:

SDK reference

Passkey adds a web-only surface to the CDP frontend SDKs.

Hooks (@coinbase/cdp-hooks)

Action hooks expose a non-throwing action for event handlers, an *Async variant that resolves with the result and rejects on failure, and the tracked data, error, status, and reset. Query hooks run on mount and expose data, error, status, refetch, and reset.

Core functions (@coinbase/cdp-core)

enrollPasskey accepts an optional name and idempotencyKey; verifyPasskey accepts an optional idempotencyKey.
For full API details, see the cdp-hooks reference, cdp-core reference, and cdp-react reference.

Browser and platform support

  • Passkeys work in web browsers that implement WebAuthn (navigator.credentials). Use isPasskeySupported() to detect availability at runtime; it resolves to false when the browser has no usable authenticator.
  • The passkey surface ships only in the web build of @coinbase/cdp-core. React Native builds resolve the native entry point, which exports none of these functions, so offer TOTP or SMS there.
  • Authenticator support (Face ID, Touch ID, Windows Hello, security keys) depends on the user’s device and browser.

Error handling

The Core SDK passkey functions throw an MfaError carrying a code. Three codes matter for passkey:
The hooks report the same errors on error rather than throwing, except through the *Async variants.

Enrollment

Enroll users across TOTP, SMS, and passkey

Prompt Handling

Handle MFA prompts for sensitive operations

Protected Operations

Learn what triggers MFA verification

Best Practices

Security recommendations and UX considerations