javascriptbiometricswebauthn

WebAuthn API not returning expected challenge token


I am currently trying to integrate biometric logins to my webapp. I've been using the WebAuthn API to accomplish this. According to the spec and other docs I've read the server needs to provide a nonce or random string as a challenge. This challenge is then echoed back in the response as one of the many checks to determine this is legitimate. My problem is the challenge being returned is not the same as I send.

Example: Let's say my server is providing asdf as the nonce (not actually, this is just for simplicity). The challenge is then converted from a JSON string to an ArrayByte using the following code:

options.publicKey.challenge = Uint8Array.from(options.publicKey.challenge, c => c.charCodeAt(0));

This is then passed into the options object along with other items.

When the registration comes back the challenge is not asdf and is not the ArrayByte equivalent of it. It's an entirely different string.

So for this example of using "asdf" this is what is returned: YXNkZg. I would anticipate the same as I passed in.

The server-side code that I'm using to create the actual nonce is as follows:

        private static string GetNonce()
        {
            //Allocate a buffer
            var ByteArray = new byte[64];
            //Generate a cryptographically random set of bytes
            using (var Rnd = RandomNumberGenerator.Create())
            {
                Rnd.GetBytes(ByteArray);
            }
            //Base64 encode and then return
            return Convert.ToBase64String(ByteArray);
        }

Has anyone seen this or is there any guidance you can provide to determine why the challenge is returning incorrectly?


Solution

  • WebAuthn does not directly return the challenge. The base64url encoded challenge is provided in clientDataJSON and is also signed over by the authenticator.

    Recommend taking a look at https://webauthn.guide

    Also, I highly recommend using a library (such as SimpleWebAuthn) and not rolling your own passkeys implementation.