M1Reeder
M1Reeder

Reputation: 722

PublicKeyCredentials Webauthn "internal" authentication missing userHandle on Android (Android Saftynet)

I'm currently able to create publicKeyCredentials using:

navigator.credentials.create({
      challenge: Uint8Array.from('CCCCCCCCCCCCCCCCCCCCCC', c => c.charCodeAt(0)),
      rp: {
        id,
        name: 'rpName'
      },
      user: {
        id: Uint8Array.from('userHandleId', c => c.charCodeAt(0)),
        name: 'userName',
        displayName: 'DisplayName'
      },
      pubKeyCredParams: [{alg: -7, type: 'public-key'}],
      authenticatorSelection: {
        userVerification: 'discouraged',
        authenticatorAttachment: 'platform',
        requireResidentKey: true, // don't seem to do anything with android-saftynet
        residentKey: 'required' // don't seem to do anything with android-saftynet
      },
      timeout: 60000,
      attestation: 'direct'
    })

Then retrieve the credential using:

navigator.credentials.get({
        publicKey: {
          challenge: generateChallenge(),
          allowCredentials: [{
            id: decodeArray(id),
            type: 'public-key'
          }],
          timeout: 60000,
          transport: ['internal'],
          userVerification: 'discouraged'
        },
      })

This flow works on all of my apple devices, however, on Android the 'fmt' of the credentials is 'android-saftynet' which doesn't seem to support userHandles.

Are there any formats I can force on Android that I can save userHandles with? Or another way to store information with the publicKeyCredential to allow me to support the usernameless webauthn flow?

EDIT: Looks like android-saftynet doesn't support userHandle. Does anyone know if there is an Android authenticator flow that supports userHandle? (aside from the USB auth flow)

Upvotes: 2

Views: 2048

Answers (2)

Rafe
Rafe

Reputation: 809

Attestation (with various formats such as android-safetynet) and usernameless flows are two completely separate concepts. You can have usernameless flows without attestation (format none).

Attestation allows you to verify the authenticator itself - whether the authenticator actually is an iPhone, Yubikey or (non-rooted) Android device, and properties like how key material is stored or what the false positive rate of the fingerprint scanner is. For more details see https://fidoalliance.org/fido-technotes-the-truth-about-attestation/ and https://medium.com/webauthnworks/webauthn-fido2-demystifying-attestation-and-mds-efc3b3cb3651

In WebAuthn level 1 usernameless registration (with requireResidentKey: true) was allowed to continue even if the user agent doesn't support this (see authenticatorMakeCredential operation step 20). In level 2 a few things changed:

  • resident keys are now referred to as client-side discoverable credentials,
  • requireResidentKey boolean was changed to the residentKey enum, but stiull supported for backwards compatibility,
  • the authenticatorMakeCredential operation was changed to return an error in step 4 if client-side discoverable credentials are not supported,
  • the credProps extension was added to determine if a client-side discoverable credential was created, either at the request of the RP or because the authenticator chose to (step 7.4).

Usernameless authentication (with an empty allowCredentials array) at the time of writing is not supported on Android using Chrome 97. This has been a known issue for a while.

Upvotes: 3

IAmKale
IAmKale

Reputation: 3426

This flow works on all of my apple devices, however, on Android the 'fmt' of the credentials is 'android-saftynet' which doesn't seem to support userHandles.

Just to clarify, the format of the attestation statement has no bearing on an authenticator's ability to return a userHandle from navigator.credentials.get(). According to the spec it's basically up to the authenticator whether it wants to return userHandle or not.

Are there any formats I can force on Android that I can save userHandles with?

WebAuthn doesn't offer any way for an RP (Relying Party, i.e. your server) to require a specific attestation station format, so you either have to support them all (for which I'd highly encourage you use an existing library) or pick and choose and reject registration of credentials with statements you don't want to support.

Or another way to store information with the publicKeyCredential to allow me to support the usernameless webauthn flow?

I'll correct this if I'm wrong but I don't think you need userHandle to accomplish usernameless. You should be able to take the credential ID you get back from navigator.credentials.get() and match it first to your list of registered credentials, and then pull the corresponding user ID from your own internal record of which credentials belong to which user.

All that to say that FIDO2 support on Android is a little spotty. Last I checked discoverable credentials are basically unsupported on the platform which means usernameless support on Android devices is pretty much out of the question for now.

Upvotes: 1

Related Questions