Skip to main content
In addition to the reference documentation below, check out our end-to-end guide on how to implement passkeys in web apps using Authsignal.

Creating a passkey

Creating a passkey must be authorized by presenting a challenge with an existing method or tracking an action to obtain a short-lived token.
const response = await authsignal.passkey.signUp({
  token: "eyJhbGciOiJ...",
  username: "jane.smith@authsignal.com",
  displayName: "Jane Smith",
});

Parameters

token
string
A short-lived token obtained by tracking an action.
username
string
The primary user identifier associated with the passkey, e.g. the user’s email address.
displayName
string
An optional secondary user identifier which the browser may display in place of or alongside the username, e.g. the user’s full name.
authenticatorAttachment
'platform' | 'cross-platform' | null
The preferred authenticator type. Defaults to platform, which targets the device’s built-in authenticator (e.g. Touch ID or Windows Hello). Set to cross-platform to target a roaming authenticator such as a security key, or null to let the user choose.
hints
string[]
An array of WebAuthn credential hints which guide the browser’s UI, e.g. ["client-device"], ["security-key"], or ["hybrid"].
useAutoRegister
boolean
Whether to use the automatic passkey upgrade flow. If true, the user’s password manager will be prompted to automatically create a passkey after successful authentication via password autofill.
useCookies
boolean
Whether to use cookies to bind the session to the browser. Defaults to false. Requires a custom API domain on the same parent domain as your site.
syncCredentials
boolean
Whether to keep the browser’s credential manager in sync with the credentials stored by Authsignal. Defaults to true. See Credential syncing for more details.

Response

response
AuthsignalResponse<SignUpResponse>

Automatic passkey upgrades

Automatic passkey upgrades are supported in Safari 18+ on macOS, all browsers on iOS 18+, Chrome 136+ on desktop, and Chrome 142+ on Android.
With passkey upgrades, your app can prompt your user’s password manager to automatically create a passkey. This works provided the user has a password saved for your app in their password manager and has recently authenticated with it. To enable this, you must set the useAutoRegister parameter to true when calling signUp.
const response = await authsignal.passkey.signUp({
  token: "eyJhbGciOiJ...",
  username: "jane.smith@authsignal.com",
  displayName: "Jane Smith",
  useAutoRegister: true,
});

Example usage

async function onSubmit(formData) {
  // Validate user on BE and return a token from `track`

  // If valid user, attempt to automatically create a passkey
  try {
    const response = await authsignal.passkey.signUp({
      token: "eyJhbGciOiJ...", // Token from `track`
      username: "jane.smith@authsignal.com",
      displayName: "Jane Smith",
      useAutoRegister: true,
    });
  } catch (error) {
    // Failed to automatically create a passkey
    console.error(error);
  }

  // Continue with post-sign-in flow
  window.location.href = "/dashboard";
}

Using a passkey

Calling signIn will present the passkey sign-in prompt. If the user successfully authenticates with their passkey, send the result token to your server to validate the challenge.
const response = await authsignal.passkey.signIn({ action: "signInWithPasskey" });

if (response.data?.token) {
  // Send the response token to your server to validate the result of the challenge
}
Check out our best practice guide for passkeys on web browsers for tips on how to implement an optimal passkey UX and avoid leading users into dead ends.

Parameters

action
string
A string which determines how the action associated with the passkey sign-in attempt will be named in the Authsignal Portal. Values are validated with the following regex: ^[a-zA-Z0-9_-]{(1, 64)}$. Cannot be combined with token.
token
string
A short-lived token obtained by tracking an action. Use this to authorize the sign-in attempt instead of action. Cannot be combined with action or autofill.
autofill
boolean
Whether to use passkey autofill (browser conditional UI). Defaults to false. Cannot be combined with token or preferImmediatelyAvailableCredentials.
onVerificationStarted
function
A callback invoked after the user selects a passkey and the browser ceremony completes, just before the result is verified with the Authsignal API. Useful for showing a loading state.
preferImmediatelyAvailableCredentials
boolean
Whether to use immediate mediation UI mode, which only prompts the user if a passkey is immediately available on the device. Defaults to false. See Immediately available credentials for more details.
useCookies
boolean
Whether to use cookies to bind the session to the browser. Defaults to false. Requires a custom API domain on the same parent domain as your site.
syncCredentials
boolean
Whether to keep the browser’s credential manager in sync with the credentials stored by Authsignal. Defaults to true. See Credential syncing for more details.

Response

response
AuthsignalResponse<SignInResponse>

Using passkey autofill

This feature requires rendering a text input with the attribute autocomplete="username webauthn":
<input type="text" id="username" autocomplete="username webauthn" />
It can also be added to a password input:
<input type="password" id="password" autocomplete="current-password webauthn" />
The webauthn value must be the last value in the autocomplete attribute, otherwise the browser will not autofill the passkey.
Then when the page loads you should initialize the input for passkey autofill by running the following code.
authsignal.passkey
  .signIn({
    action: "signInWithPasskeyAutofill",
    autofill: true,
  })
  .then((response) => {
    if (response.data?.token) {
      // The user has focused your text input and authenticated with an existing passkey
      // Send the response token to your server to validate the result of the challenge
    }
  });

Immediately available credentials

Setting preferImmediatelyAvailableCredentials to true uses the browser’s immediate mediation UI mode. The passkey prompt is only shown if a credential is immediately available on the device, otherwise the call returns without interrupting the user. This is useful for surfacing a passkey prompt automatically on page load without showing an error to users who don’t have a passkey.
const response = await authsignal.passkey.signIn({
  action: "signInWithPasskey",
  preferImmediatelyAvailableCredentials: true,
});

if (response.errorCode === "credential_not_found") {
  // No passkey was immediately available, fall back to another method
} else if (response.data?.token) {
  // Send the response token to your server to validate the result of the challenge
}
Immediate mediation UI mode is only supported in browsers that implement the WebAuthn getClientCapabilities and immediate get APIs. When unsupported, the call returns the immediate_mediation_not_supported error code. It also cannot be combined with autofill.

Credential syncing

A common source of passkey friction is stale credentials: a passkey is deleted on the server but still appears in the user’s browser or password manager, so they select it and hit a dead end. To prevent this, the SDK uses the WebAuthn Signal API to keep the browser’s credential manager in sync with the passkeys stored by Authsignal. This happens automatically while syncCredentials is true (the default), so there’s no separate method to call:
  • After a successful signUp or signIn, the SDK calls signalAllAcceptedCredentials with the full set of the user’s valid credentials. The browser removes any passkeys for your app that are no longer on this list.
  • When signIn fails because the presented passkey is no longer recognized by the server (the unknown_credential error code), the SDK calls signalUnknownCredential so the browser removes that specific passkey.
Set syncCredentials to false on signUp or signIn to opt out.
The Signal API is only available in supporting browsers. Where it isn’t supported, syncing is silently skipped and sign-in is unaffected.

Passkey error handling

When any of the underlying native browser WebAuthn APIs fail, the SDK will throw a WebAuthnError. Most of these errors are benign and can be safely ignored. However, if you wish to handle them, you can do so by catching the error in a try/catch block. Common errors you will encounter during passkey authentication are:
  • ERROR_CEREMONY_ABORTED - The user cancelled the passkey ceremony. We recommend ignoring this error as it’s a normal part of the user experience.
  • ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED - The user already has a passkey registered for this authenticator (exclusive to signUp). For example, the user is trying to create an iCloud Keychain passkey but already has one registered in iCloud Keychain.
During development, you may also encounter ERROR_INVALID_RP_ID which occurs when the relying party ID is invalid for the domain.
import { WebAuthnError } from "@authsignal/browser";

try {
  const response = await authsignal.passkey.signUp({
    token: "eyJhbGciOiJ...",
    username: "jane.smith@authsignal.com",
    displayName: "Jane Smith",
  });
} catch (error) {
  if (error instanceof WebAuthnError) {
    if (error.code === "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED") {
      // The user already has a passkey registered for this device
      // You can choose to handle this however you see fit
    }
  }
}