2

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?
simbro
  • 3,372
  • 7
  • 34
  • 46

2 Answers2

8

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.

Luke Walker
  • 333
  • 1
  • 4
0

I have tried to understand what you are dealing with. I played with https://u2f.bin.coffee/ to get a feeling for the data flow. As a result of authentication I have received a response like:

Got response:
{
  "keyHandle": "F74UNCdNv1d43zw7hqxYgkjR3O6dcevopiSb3jrcB3rMFRUM486LbsVExJD0R3ESC5MCb3zeFGdxvS3ksZ7sCA",
  "clientData": "eyJ0eXAiOiJuYXZpZ2F0b3IuaWQuZ2V0QXNzZXJ0aW9uIiwiY2hhbGxlbmdlIjoiTXpPTjhXRHpvSDlhZHU0bTk5YWF0ZyIsIm9yaWdpbiI6Imh0dHBzOi8vdTJmLmJpbi5jb2ZmZWUiLCJjcm9zc09yaWdpbiI6ZmFsc2UsImV4dHJhX2tleXNfbWF5X2JlX2FkZGVkX2hlcmUiOiJkbyBub3QgY29tcGFyZSBjbGllbnREYXRhSlNPTiBhZ2FpbnN0IGEgdGVtcGxhdGUuIFNlZSBodHRwczovL2dvby5nbC95YWJQZXgifQ",
  "signatureData": "AQAAAAUwRAIgEqi5POKKUraU97W3vbfn34DSWqXwiZwEi5g9QPPtS6MCIBbLYW1_b3aRjHQivSRZQUAfBobx6CZnQ0_VVvuu1LJJ"
}

Now I assume the keyHandle here is your authenticatorData, the clientData here is your clientDataJSON and that signatureData is your signature. Whatever this userHandle is you are missing, it does not seem to be required.

Look at this picture too:

Taken from the page https://developers.yubico.com/U2F/Protocol_details/Overview.html

If the userHandle were the handle, the authentication would not work with a null value. But it does if I understand your example correctly.

So I believe you are dealing with a field that is reserved for future purposes or other flows than that which you need at the moment.

Marek Puchalski
  • 3,286
  • 2
  • 26
  • 35
  • 1
    Thanks for looking into it and replying in such detail. The userHandle property is indeed apart of the WebAuthn spec but may be be broadly implemented. I will need to dig further on this I think. – simbro Jul 07 '20 at 10:58