MasterKale / SimpleWebAuthn

WebAuthn, Simplified. A collection of TypeScript-first libraries for simpler WebAuthn integration. Supports modern browsers, Node, Deno, and more.
https://simplewebauthn.dev
MIT License
1.62k stars 137 forks source link

`verifyAuthenticationResponse` TypeError: cosePublicKey.get is not a function #458

Closed H3RSKO closed 1 year ago

H3RSKO commented 1 year ago

Describe the issue

When trying to verify an authenticator with verifyAuthenticationResponse, i'm getting TypeError: cosePublicKey.get is not a function.

Stack trace:

Screenshot 2023-10-13 at 4 49 03 PM

Reproduction Steps

The registration flow works as expected, but I cannot complete the final verifyAuthenticationResponse step.

This is the data i am passing into verifyAuthenticationResponse:

{
  response: {
    id: 'zoJI0Aa2dOb2a4qx3Kzm0IjGgxl8ZBnXf1Ta6Om4gEI',
    rawId: 'zoJI0Aa2dOb2a4qx3Kzm0IjGgxl8ZBnXf1Ta6Om4gEI',
    response: {
      authenticatorData: 'YkwHDjAspXeq2sn_IIWVunr0oFvhhtyCJubZrA56lk0FAAAAAA',
      clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiX3pmTTRLZ2s1WEk3Y1EyQmo4RURaQWtHQnZ0d2RqQnpadm5TZmVNSHlTWSIsIm9yaWdpbiI6Imh0dHBzOi8vbXlsb2NhbHdlYnNpdGUiLCJjcm9zc09yaWdpbiI6ZmFsc2V9',
      signature: 'MEUCIFAsp5nX_KwwguG6LI5PisY1R4rYPi0eAPiqjRstGj6cAiEA7Au2txVKIP58A1RkFHvNEthhM22tmO7JaYfVoogF2TQ',
      userHandle: '7a8da293-bc06-43a8-b7ab-0a27f14e3c82'
    },
    type: 'public-key',
    clientExtensionResults: {},
    authenticatorAttachment: 'platform'
  },
  expectedChallenge: '_zfM4Kgk5XI7cQ2Bj8EDZAkGBvtwdjBzZvnSfeMHySY',
  expectedOrigin: 'https://mylocalwebsite',
  expectedRPID: 'mylocalwebsite',
  requireUserVerification: true,
  authenticator: {
    credentialId: Uint8Array(32) [
              206, 130,  72, 208,   6, 182, 116,
              230, 246, 107, 138, 177, 220, 172,
              230, 208, 136, 198, 131,  25, 124,
              100,  25, 215, 127,  84, 218, 232,
              233, 184, 128,  66
            ],
            credentialPublicKey: Uint8Array(264) [
              49, 54, 53, 44, 49, 44, 50, 44, 51, 44, 51, 56,
              44, 51, 50, 44, 49, 44, 51, 51, 44, 56, 56, 44,
              51, 50, 44, 49, 52, 54, 44, 54, 55, 44, 49, 57,
              44, 52, 57, 44, 52, 51, 44, 50, 50, 50, 44, 49,
              53, 52, 44, 49, 56, 51, 44, 49, 54, 55, 44, 49,
              55, 44, 49, 56, 53, 44, 49, 57, 57, 44, 49, 49,
              57, 44, 50, 52, 55, 44, 50, 50, 52, 44, 50, 56,
              44, 55, 56, 44, 50, 50, 57, 44, 49, 48, 50, 44,
              49, 54, 44, 54,
              ... 164 more items
            ],
    counter: '0',
    transports: [ 'internal' ]
  }
}

The parsed credentials from the startRegistration() JSON output.

Screenshot 2023-10-15 at 10 41 35 PM

Additionally, I occasionally run into this error, I assume it is connected. RangeError: Offset is outside the bounds of the DataView

Screenshot 2023-10-16 at 9 19 29 AM

Expected behavior

It should correctly verify the passkey.

Code Samples + WebAuthn Options and Responses

Dependencies

SimpleWebAuthn Libraries

$ npm list --depth=0 | grep @simplewebauthn
├── @simplewebauthn/browser@8.3.1
├── @simplewebauthn/server@8.2.0
# ...

Additional context

Thank you!

H3RSKO commented 1 year ago

Figured out the issue.

I'm using postgres, and when storing the raw credentialPublicKey Uint8Array as a BYTEA, it get's modified. Pulling it from the db as a buffer and then converting back to a Uint8Array for verification will result in a different (much larger) Uint8Array.

The fix is the make sure to convert the Uint8Array to a buffer before saving it to the db. This will maintain the Uint8Array's integrity throughout the flow.

Hope this helps anyone else who may encounter this issue.

MasterKale commented 1 year ago

...when storing the raw credentialPublicKey Uint8Array as a BYTEA...

@H3RSKO I'm glad you were able to figure this out. Is there bad advice in the comments of the data structures I lay out in the docs for RP's to define in their DB?

// SQL: Store raw bytes as `BYTEA`/`BLOB`/etc...
credentialPublicKey: Uint8Array;

https://simplewebauthn.dev/docs/packages/server#additional-data-structures

If so, I'd welcome suggestions for improvement here. I don't want to continue steering people into a common problem with my recommendations if this is bad advice.

H3RSKO commented 1 year ago

@MasterKale Yeah, I think we just need to be clear about how the datatypes need to be modified based on where we are using them in the flow. For example the credentialPublicKey is going through the following changes:

Also, another issue I ran into that others have encountered is that even though we are storing credentialID as a base64Url, generateAuthenticationOptions requires it to be a buffer, and then converts it back to a base64Url. If it is passed in as a base64Url it will return the authenticator with and empty id field.

Maybe update generateAuthenticationOptions to something like this so it works for both base64url and buffer.

allowCredentials: allowCredentials?.map((cred) => ({
            ...cred,
            id: typeof(cred.id !== 'string') ? index_js_1.isoBase64URL.fromBuffer(cred.id) : cred.id,
        })),

If you like i can put together a PR on the docs for those datatype issues, not sure what you want to do with the generateAuthenticationOptions one.

codegod100 commented 5 months ago

was running into same issue until I realized the library I'm using stores blob data as Buffer type, so when I stored I changed to Buffer.from(publicKey)