Published: November 12, 2024, Last updated: November 29, 2024
The WebAuthn Signal API allows relying parties to signal existing credentials to connected passkey providers. With this, a supporting passkey provider can update or remove incorrect or revoked passkeys from its storage so they are no longer offered to users.
Chrome on desktop supports Signal API starting from Chrome 132. Google Password Manager can update passkeys reflecting the signal. For Chrome extension based passkey providers, it's up to them whether they will reflect the signal or not.
Chrome on Android support is coming later.
Safari is supportive but not implemented yet. Firefox hasn't shared their opinions yet.
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 the user to identify which of the offered passkeys to sign in with when prompted. This is especially useful when they have more than two passkeys from different passkey providers.
However, there are a couple of cases where inconsistencies between the passkey provider's passkey list and the server's credentials list can lead to confusion.
The first case is when a user deletes a credential on the server leaving the passkey in the passkey provider untouched. The next time the user tries to sign in with a passkey, that passkey will still be presented to the user by the passkey provider. However, the attempt to sign in will fail because the server won't be able to verify with the public key which was deleted.
The second case is when a user updates their username or the 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 despite it's updated on the server. Ideally they should be in sync.
The Signal API is a WebAuthn API that resolves these confusions by allowing RPs to signal changes to the passkey provider. There are three methods:
PublicKeyCredential.signalUnknownCredential
: Signal that a credential does not existPublicKeyCredential.signalAllAcceptedCredentials
: Signal a list of saved credentialsPublicKeyCredential.signalCurrentUserDetails
: Signal updated username and/or display nameconstcredential=awaitnavigator.credentials.get({...});constpayload=credential.toJSON();constresult=awaitfetch('/login',{...});// Detect authentication failure due to lack of the credentialif(result.status===404){// Feature detectionif(PublicKeyCredential.signalUnknownCredential){awaitPublicKeyCredential.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. It's up to the passkey provider how to deal with this signal, but the associated passkey is expected to be removed so that the user won't sign in with a passkey as the associated credential does not exist.
This API can be invoked when a passkey based sign-in has failed due to an absence of a credential. This way, the RP can prevent the user from attempting to sign in with a passkey that doesn't have an associated credential. Unlike signalAllAcceptedCredentials
, this method does not require passing the entire list of credential IDs, so it should be used whenever the user is not authenticated to avoid revealing the number of passkeys for a given user.
// After a user deletes a passkey or a user is signed in.// Feature detectionif(PublicKeyCredential.signalAllAcceptedCredentials){awaitPublicKeyCredential.signalAllAcceptedCredentials({rpId:"example.com",userId:"M2YPl-KGnA8",// base64url encoded user IDallAcceptedCredentialIds:[// A list of base64url encoded credential IDs"vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA",...]});}
By calling PublicKeyCredential.signalAllAcceptedCredentials()
with an RP ID, a user ID and a list of credential ID of stored credentials, the RP can inform the passkey provider of the remaining credentials in its storage. It's up to the passkey provider how to deal with this signal, but the passkeys that don't match this list are expected to be removed so that the user won't see passkeys on sign in for which the associated credential does not exist.
This API should be invoked when a user deletes a passkey on the RP and on every sign-in, so that the passkey provider can keep a synchronized list of passkeys.
// After a user updated their username and/or display name// or a user is signed in.// Feature detectionif(PublicKeyCredential.signalCurrentUserDetails){awaitPublicKeyCredential.signalCurrentUserDetails({rpId:"example.com",userId:"M2YPl-KGnA8",// base64url encoded user IDname:"a.new.email.address@example.com",// usernamedisplayName:"J. Doe"});}else{}
By calling PublicKeyCredential.signalCurrentUserDetails()
with an RP ID, a user ID, a username and a display name, the RP can inform the passkey provider of the updated user information. It's up to the passkey provider how to deal with this signal, but the passkeys the user owns are expected to be updated with the new user information.
This API can be invoked when the user's username or display name are updated, and on every sign in, so that the passkey provider can keep this information synchronized with the server.
The Signal API helps you build a better passkey experience by eliminating chances of unexpected sign-in failure. With Signal API, relying parties can signal the list of existing credentials and their metadata, so they can keep passkeys on the passkey provider in-sync.
To learn more about passkeys, start from Passwordless login with passkeys.
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2024-11-12 UTC.