Search code examples
securitywebauthnyubikey

YubiKey + Webauth: userHandle is always null


When I authenticate using WebAuthn and my YubiKey, the response.userHandle property is always null. That is the user id and displayName that I registered the credential with does not get returned. Is this becuase of something I am doing wrong during the registration / authentication process:

async function register() {
  const publicKeyCredentialCreationOptions = {
    challenge: Uint8Array.from("this-is-a-test", (c) => c.charCodeAt(0)),
    rp: {
      name: "Webauthn Test",
      id: "localhost",
    },
    user: {
      id: Uint8Array.from("a1b2c3d4e5f6", (c) => c.charCodeAt(0)),
      name: "just-a-test",
      displayName: "MrUser",
    },
    pubKeyCredParams: [{ alg: -7, type: "public-key" }],
    authenticatorSelection: {
      authenticatorAttachment: "cross-platform",
    },
    timeout: 60000,
    attestation: "direct",
  };

  const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions,
  });
}

This is the code I use to authenticate:

async function authenticate() {
  const publicKeyCredentialRequestOptions = {
    challenge: Uint8Array.from("test", (c) => c.charCodeAt(0)),
    allowCredentials: [
      {
        id: credentialId,
        type: "public-key",
        transports: ["usb", "ble", "nfc"],
      },
    ],
    timeout: 60000,
  };

  const assertion = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
  });

  console.log(assertion);
}

What I end up with is:

{
  rawId: ArrayBuffer(64),
  id: "U-nitqhlORmmdltp7TLO3i18KNoWsSebFyrtc3OIRvcktvwlz-dJZCA1_1gxXrNHzqReU7xGAHdfVP75N2aJSw", 
  response: {
    authenticatorData: ArrayBuffer(37) {}
    clientDataJSON: ArrayBuffer(101) {}
    signature: ArrayBuffer(71) {}
    userHandle: null
  }
  type: "public-key"
}

As you can see: userHandle is null.  Can anyone tell me why?

Solution

  • The userHandle can be null depending on which type of WebAuthn credential the relying party requested to be created.

    The default WebAuthn behavior will create a non-discoverable credential and the userHandle returned in the assertion will be null. No data is stored on the authenticator for this type of credential so there is nothing to return.

    To create a WebAuthn client-side discoverable credential, a.k.a. resident key, you must set the requireResidentKey member to true. This will store credential data on the authenticator and will return the userHandle in the assertion. Refer to the AuthenticatorSelectionCriteria in the W3C WebAuthn spec for the details.

    Here is an example:

    authenticatorSelection: {
      authenticatorAttachment: "cross-platform",
      requireResidentKey: true
    },
    

    See Yubico's WebAuthn Dev Guide to learn more about resident keys and the userHandle.