Keep passkeys consistent with credentials on your server with the Signal API

Published: November 12, 2024, Last updated: November 29, 2024

The WebAuthn Signal API lets relying parties signal existing credentials to connected passkey providers. This lets a supporting passkey provider update or remove incorrect or revoked passkeys from its storage so users are no longer offered them.

Compatibility

Chrome supports Signal API on all desktop platforms and Android.

Safari is supportive but not yet implemented. Firefox hasn't yet shared its opinion.

Google Password Manager can update passkeys to reflect the signal. Chrome extension-based passkey providers on desktop decide whether to reflect the signal.

Background

When a passkey (a discoverable credential) is created, metadata such as a username and a display name are saved to the passkey provider (such as a password manager) along with the private key, while the public key credential is saved to the relying party's (RP's) server. Saving the username and display name helps users identify which offered passkeys to use for sign-in when prompted. This is especially useful when users have more than two passkeys from different passkey providers.

However, there are a few cases where inconsistencies between the passkey provider's passkey list and the server's credentials list can cause confusion.

The first case is when a user deletes a credential on the server. This leaves the passkey in the passkey provider untouched. The next time the user tries to sign in with a passkey, the passkey provider still presents that passkey to the user. However, the attempt to sign in will fail because the server cannot verify the public key that was deleted.

The second case is when a user updates their username or display name on the server. The next time the user tries to sign in, the passkey in the passkey provider continues to display the old username and display name even though it's updated on the server. Ideally, these are synchronized.

Signal API

The Signal API is a WebAuthn API that resolves these inconsistencies by letting RPs signal changes to the passkey provider. There are three methods:

Signal that a credential does not exist

const credential = await navigator.credentials.get({ ... });
const payload = credential.toJSON();

const result = await fetch('/login', { ... });

// Detect authentication failure due to lack of the credential
if (result.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

By calling PublicKeyCredential.signalUnknownCredential() with an RP ID and a credential ID, the RP can inform the passkey provider that the specified credential has been removed or does not exist. The passkey provider determines how to handle this signal, but the associated passkey should be removed so that users don't try to sign in with a passkey that lacks an associated credential.

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

This API can be invoked when a passkey-based sign-in has failed because a credential is absent. This way, the RP can prevent users from attempting to sign in with a passkey that doesn't have an associated credential. Unlike signalAllAcceptedCredentials, this method does not require you to pass the entire list of credential IDs, so use it whenever the user is not authenticated to avoid revealing the number of passkeys for a given user.

A dialog displayed when a passkey is deleted from Google Password Manager on Chrome.
A dialog displayed when a passkey is deleted from Google Password Manager on Chrome.

Signal a list of saved credentials

// After a user deletes a passkey or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalAllAcceptedCredentials) {
  await PublicKeyCredential.signalAllAcceptedCredentials({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    allAcceptedCredentialIds: [ // A list of base64url encoded credential IDs
      "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA",
      ...
    ]
  });
}

Use PublicKeyCredential.signalAllAcceptedCredentials() after a user signs in or manages account settings. You provide a list of all valid credential IDs for that user. The passkey provider compares this list against its local storage for that relying party. The passkey provider marks as "hidden" any passkey found in its storage that is not included in the allAcceptedCredentialIds list. The system no longer offers these hidden passkeys for sign-in or autofill, but they are not immediately deleted permanently, which allows for restoration if necessary. Conversely, the passkey provider restores passkeys present in allAcceptedCredentialIds that are marked as "hidden". This lets your website restore passkeys that were hidden in error.

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

Invoke this API when a user deletes a passkey on the RP and on every sign-in, so the passkey provider can maintain a synchronized list of passkeys.

Signal updated username and display name

// After a user updated their username and/or display name
// or a user is signed in.

// Feature detection
if (PublicKeyCredential.signalCurrentUserDetails) {
  await PublicKeyCredential.signalCurrentUserDetails({
    rpId: "example.com",
    userId: "M2YPl-KGnA8", // base64url encoded user ID
    name: "a.new.email.address@example.com", // username
    displayName: "J. Doe"
  });
} else {
}

By calling PublicKeyCredential.signalCurrentUserDetails() with an RP ID, a user ID, a username and display name, the RP can inform the passkey provider about the updated user information. The passkey provider determines how to handle this signal, but it should update the passkeys the user owns with the new user information.

Browser Support

  • Chrome: 132.
  • Edge: 132.
  • Firefox: not supported.
  • Safari: 26.

Source

This API can be invoked when the user's username or display name is updated, and on every sign in, so that the passkey provider can maintain this information synchronized with the server.

A dialog displayed when a passkey metadata is updated in Google Password Manager on Chrome.
A dialog displayed when a passkey metadata is updated in Google Password Manager on Chrome.

Summary

The Signal API helps you build a better passkey experience by eliminating unexpected sign-in failures. The Signal API lets relying parties signal the list of existing credentials and their metadata, so they can keep passkeys on the passkey provider synchronized.

To learn more about passkeys, see Passwordless login with passkeys.