Search code examples
google-chromewebauthn

Webauthn - allowCredentials and credential selection


I am implementing Webauthn where a browser-based client is communicating to a Java back-end using Webauthn4J. In the back-end, I have an association between a registered user and credentials linked to that user. For the authentication flow, I am populating the allowCredentials array with the credentials linked to the user which are active.

For the purposes of explaining the problem, assume user1 has 3 credentials linked, c1, c2 and c3. What I am seeing on Chrome (v113.0.5672.126, macOS Ventura 13.3.1) is that a single credential is picked from the list of allowCredentials and the user is prompted to authenticate that credential. The way the credential is selected doesn't seem deterministic, it is not the newest or the oldest and is unrelated (from what I've understood) to the order of the credentials listed in the allowCredentials array. If I delete that credential from Chrome's passkeys, then a different credential is selected from the list, but again, only a single credential is presented for authentication.

If I remove the list of allowCredentials, the user is prompted with all 3 credentials for authentication as all 3 credentials are scoped to the domain. I would expect similar behaviour when using allowCredentials with all 3 credentials included.

I've verified this behaviour using Webauthn debugger so it doesn't seem to be an implementation problem on my side.

The specification states: "The list is ordered in descending order of preference: the first item in the list is the most preferred credential, and the last is the least preferred."

This is not what I am seeing in practice. Neither the order is respected, nor are all credentials included in the prompt.

Am I misunderstanding how allowCredentials are supposed to work or is this a quirk of the Chrome implementation of Webauthn?


Solution

  • The issue might be related to the number of credentials that you're attempting to generate on the same device for the same user for the same app/website. While a user should be allowed to have multiple passkeys to access an account, they ideally should only have one passkey per app on a device (I'm using the term device which could be an authenticator or provider like Google/Apple).

    • Encouraged: User A can have their App 1 passkeys on Device A, and Device B
    • Encouraged: Device A can have App 1 passkeys for User A and User B
    • Discouraged: Device A has multiple User A passkeys for App 1

    You should leverage the excludeCredentials list to prevent this scenario from your client application. An error/alert should be thrown for a user attempting to create a new passkey on a device where they already have one.

    On to the second issue on the use of the allowCredentials list not allowing you to select your passkey when populated. My understanding of this is fuzzy, but as I understand it; when a credentialID is populated in the allowCredentials list, the client (in this case Chrome) will attempt to find that specific credential (whether it's part of Chrome's passkeys or from an external authenticator like a security key). If a credential matching the credentialID is found, Chrome will immediately just prompt for it. Overall this isn't an issue because your app is just looking for any passkey that matches the criteria, it doesn't necessarily matter which you're selecting.

    When you don't provide an allowCredentials list, Chrome doesn't know which specific credential that you're looking for. In this case the browser will prompt you to select the specific credential (passkey) that you want to use, because one was never hinted to the browser.

    Hope this helps.