Passkeys (WebAuthn)

Biometrics within the context of WebAuthn lets you authenticate end users using the strong authenticators that are now often built right into devices, including biometrics (fingerprint, facial, or iris recognition) and secure hardware keys (passkeys) like those provided by Yubico, CryptoTrust, or Thedis.

Try out Passkeys for yourself, at Passkeys Guru!

Domain Specific Passkeys

Descope's passkey implementation is domain-specific, which means that passkeys are tied to the domain where they were created. In scenarios where you have multiple applications on multiple different domains using the same Descope project, you can utilize the user.webAuthn key in a flow condition to prompt for passkeys. Descope will check whether a valid passkey has been created for the specific domain the user is on. If true, it means that a passkey has been stored for the current domain only.

passkey domain specific

Physical Keys

Passkeys can also be implemented using physical security keys like YubiKeys. To learn more about YubiKey authentication, check out our blog here.

Passkeys with Flows

This guide will walk you through integrating Passkey-based authentication into your Descope Flows. Passkeys offer a secure, passwordless login experience based on FIDO2 and WebAuthn standards.

Flow Screens

When using Passkeys, you can use the Passkey component to enable them in your Flow screens. There is also the Biometrics action, which also operates according to the WebAuthn standards, but enforces the use of biometrics to login specifically.

Passkey Autofill

Passkeys rely on a unique identifier to associate them with a user. This is typically a phone number or email address. Descope offers an autofill feature that will allow the user to select their Passkeys that exists on the associated domain, without having to type it in.

enable-passkey-autofill

Flow Actions

When using Passkeys, the following actions are available:

  • Sign Up / Passkeys - Registers a new user with a Passkey; will fail if user already exists.
  • Sign Up or In / Passkeys - Registers or authenticates the user with a Passkey.
  • Sign In / Passkeys - Authenticates existing user with a Passkey; will fail if user does not exist.
  • Update User / Passkeys - Updates the user's information after Passkey authentication.

Each of these actions supports standard error handling, including scenarios where a user cancels the passkey prompt (e.g., closing the authentication window or dismissing the biometric dialog). In these cases, an "authentication aborted" error is exposed and can be handled as part of your flow logic.

How to Use Passkeys Actions

To learn more about Actions in general, you can refer to our guide on them.

These actions are straightforward and can be integrated into your application like any other Action.

This is an example of using the Authenticate with Passkey action in a flow:

authenticate-with-passkey-flow-action

Restrict Types of Passkeys

When using Passkeys, you as the Descoper can restrict the types of Passkeys allowed for the user to sign up or in.

  • Platform Passkeys - Passkeys that are stored on the local device or profile of the user.
  • External Passkeys - Passkeys stored on other devices that are shared via security keys or mobile phones over Bluetooth.

You can control this restriction in the dropdown here, underneath the Passkey flow actions:

passkey-flow-action

Allow Passkeys Without User Verification

Enabling this will allow your users to authenticate with passkeys, without using biometrics or by using a security key without a PIN.

Check if Device Supports WebAuthn

Before allowing a user to authenticate with passkeys, you may want to check if the user's device supports WebAuthn, and have them authenticate with a different method if not. You can use the device.webAuthnSupport key within a condition to check this.

check if webauthn supported

Multiple Passkeys per User

Descope doesn't limit a user to a single passkey. Each successful Update User / Passkeys action registers an additional passkey on the user's account — for example, one per device or browser — without affecting any passkeys the user already has. This lets users sign in with whichever device is on hand, rather than being tied to the one they originally signed up with.

End users can view every passkey registered to their account, and remove a specific one, through the User Profile Widget.

Customizing the Passkey Login ID and Label

By default, a new passkey is bound to and labeled with whichever login ID the user most recently authenticated or stepped up with — for example, a phone number, even if the user's account is primarily identified by email. Two optional inputs let a flow override this, both accepting dynamic values (for example, {{form.externalId}}) and leaving the previous behavior in place if left unset:

  • Passkey Label — available on Sign Up, Sign Up or In, and Update User / Passkeys. Sets the label shown in the browser or OS keychain, independent of the user's actual profile name — for example, labeling a passkey "Work Laptop" instead of the user's email ID.
  • Passkey Login ID — available only on Update User / Passkeys, the action used when a user has already authenticated with an existing method (like a password or OTP) and is now adding a passkey. Choose which of that user's existing login IDs the new passkey binds to, instead of defaulting to the login ID from the most recent auth step. Must be one of the user's own login IDs — Descope validates this server-side, so a flow can't bind a passkey to a login ID the authenticated user doesn't own.

On Sign Up and Sign Up or In, only Passkey Label is available, since the passkey action itself is the authentication event — there's no already-known user to choose an existing login ID from:

Passkey Label field on the Sign Up or In / Passkeys action

Update User / Passkeys additionally offers Passkey Login ID, since that's the action used once a user has already authenticated and is adding a passkey to their account:

Passkey Login ID and Passkey Label fields on the Update User / Passkeys action

Note

If a dynamic value set in either field fails to resolve (for example, it references a key that doesn't exist), Descope falls back to the previous default behavior rather than using an empty or invalid value.

Error Handling

Passkey errors are handled like any other action. You can refer to our Flow Error Handling guide for more details.

Passkey Failure Logging

When a WebAuthn prompt fails or is canceled during a web Flow, Descope captures failure context from the browser and writes it to the Troubleshooting Logs. This applies to all passkey finish actions: Sign Up, Sign In, Sign Up or In, and Update User.

Note

Logged failure details are automatically sent by web browser Flows (React, Next.js, Vue, Angular, and HTML). Direct SDK calls and native mobile Flows do not send server logs. For direct SDK error handling, see Handle Passkey Errors.

Field sent by the web componentField in the Troubleshooting LogsDescription
failurefailureThe DOMException name returned by the browser, such as NotAllowedError.
failureReasonfailure_reasonThe failure category computed by the Descope SDK. See Failure Reason Values.
failureMessagefailure_messageThe DOMException message returned by the browser.

Descope truncates failure_message to 256 characters before logging it.

Failure Reason Values

The SDK checks these conditions in order and uses the first value whose condition matches the failure.

failure_reasonBrowser errorWhat it means
abortedAbortErrorThe SDK canceled the prompt, for example after the web component's 90-second timeout.
authenticator_already_registeredInvalidStateErrorThe device already has a passkey for this user. Registration only.
no_supported_algorithmNotSupportedErrorThe authenticator supports none of the requested algorithms.
authenticator_constraints_unsupportedConstraintErrorThe authenticator can't meet the requested options, such as a resident key or user verification.
invalid_rpidSecurityErrorThe page's domain doesn't match the passkey Top Level Domain.
invalid_domainSecurityErrorThe page isn't on a valid domain, for example an IP address.
webauthn_unsupportedAnyThe browser doesn't support WebAuthn.
platform_authenticator_unsupportedAnyA built-in authenticator, such as Touch ID or Windows Hello, was required but isn't available. Registration only.
not_allowedNotAllowedErrorThe user canceled, the prompt timed out, or no matching passkey was found. Browsers don't say which.
unknownAny other errorNo other value matched.

Passkey already on the device: When a user tries to register a passkey that already exists on the device, the browser returns an InvalidStateError and Descope logs failure_reason as authenticator_already_registered. The Update User / Passkeys action shows the user the message "This device has already been added as a passkey." The Sign Up and Sign Up or In actions show the generic passkey error instead.

Was this helpful?

On this page