skip to Main Content

I’m currently working on an authentication flow based in web authentication. I’ve a yubikey 5 device for testing it.

For the authentication ceremony, I’m using the following settings:

  • allowCredentials: [] (an empty array, as I want to use discoverable credentials)
  • userVerification: discouraged (in the interest of minimizing disruption to the user interaction flow)

What I see is that when using Chrome with those settings, the device PIN is always requested to the user. This behavior is different in Firefox. In that case the PIN is not being requested to the user.

This can be easily reproduced in this demo site: https://webauthn.io/

Does anybody have an idea about why the behavior is different between both browsers? Could this be a bug in Chrome?

Thank you in advance.

Note: tested with versions:

  • Chrome 123.0.6312.86 (Official Build) (64-bit)
  • Firefox 124.0 (64-bit)

2

Answers


  1. When passkeys are created on a security key in Chrome with UV=required or preferred, they are created with credProtect level 3, meaning that user verification is required when the credential is used.

    In this case, I’m assuming that you created the passkey with UV=discouraged?

    Since you are not using an allowlist and the security key has UV configured, Chrome is likely forcing user verification as passkeys are meant to be a primary sign in credentials (e.g. they’re usernameless and multifactor). You don’t want a user picking up a security key off the ground and gaining full access to someone’s accounts.

    Since you’re using this in more of a second factor / step up style flow, can you test with an allowlist and see if you’re still asked for UV?

    Login or Signup to reply.
  2. There are subtle differences in how Chrome has implemented Webauthn vs how Firefox has done it. If I generate a request through Firefox Webauthn will trigger the following CTAP request

    {
      1 => <Buffer 34 9d 9d c8 ef 9a c9 36 b3 3c dd f9 54 aa 57 b4 b1 f0 52 dd c5 7f 86 2e 37 9e ff b4 57 f7 48 57>,
      2 => {
        id: 'webauthn.io',
        name: 'webauthn.io'
      },
      3 => {
        displayName: 'abcde',
        id: <Buffer 58 4c 54 67 73 77 4e 6a 37 34 5f 49 62 4d 75 68 67 47 74 43 5a 4c 6f 67 55 48 50 72 63 4d 49 4c 51 45 72 78 77 73 64 37 35 48 51>,
        name: 'abcde'
      },
      4 => [
        {
          alg: -7,
          type: 'public-key'
        },
        {
          alg: -257,
          type: 'public-key'
        }
      ],
      7 => {
        rk: true
      },
      8 => <Buffer c3 3b a4 9b 74 06 ae e8 76 df 66 a7 57 4e 6b 00 a9 a2 00 e4 ce 40 a4 d4 89 40 06 92 bd a2 80 10>,
      9 => 2
    }
    

    This request as seen contains what you’d expect, the request itself has rk = true which would make a discoverable credential on the device. The request has a token, but that only affects our creation and not the assertion afterwards.

    If I make the same request in Chrome however I get the following request

    {
      1 => <Buffer b9 da 62 ad 82 89 4d 87 c8 8c b2 e0 a2 2d 74 23 ef a2 af 04 a9 16 06 76 d6 c4 13 c0 08 04 fa 19>,
      2 => {
        id: 'webauthn.io',
        name: 'webauthn.io'
      },
      3 => {
        displayName: 'Testsetesrsdr',
        id: <Buffer 45 37 4b 4c 76 50 48 59 46 4d 42 7a 5a 5a 75 48 6f 6d 31 4f 65 6f 43 33 68 41 5f 70 44 68 73 57 56 53 45 48 32 41 39 57 71 79 4d>,
        name: 'Testsetesrsdr'
      },
      4 => [
        {
          alg: -7,
          type: 'public-key'
        },
        {
          alg: -257,
          type: 'public-key'
        }
      ],
      6 => {
        credProtect: 2
      },
      7 => {
        rk: true
      },
      8 => <Buffer 1d df 5a dc 36 8b 59 92 95 dd 07 32 c5 8b 1d e8 64 57 c1 e2 b0 67 54 22 b1 7b 50 24 b3 4d 3f 05>,
      9 => 2
    }
    

    Almost the same request, rk is also true here among other but there is one critical difference. Chrome has added a value to key 6 which is credProtect: 2. CredProtect Level 2 is defined in CTAP as userVerificationOptionalWithCredentialIDList which means the key would only be discoverable through either some sort of User Verification (in your case, the pin), or by including it in the allowList. More info on CredProtect

    I strongly doubt this is a bug in Chrome, but actually something Chrome has decided they want have as part of their implementation. Webauthn in itself does very little to cover anything regarding CredProtect so this decision doesn’t affect their conformance with the specification either.

    If this is the correct way to do it by Chrome becomes very opinion-based and it is not necessarily the right way as supplying allowList to requests comes with a lot of privacy concerns, many of whom are covered in webAuthn, especially chapter 14.6.3.

    So, unless Chrome changes, which I doubt, your only options here is to either maintain and supply the allowList or do the UserVerification ceremony. The key generated through Firefox would however be possible to get without either as the default value for CredProtect in CTAP is 1. And it would be possible to get it on either Chrome or Firefox.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search