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

Upgraded to 10 from 9 and iphone no longer to connect with chrome #575

Closed lukepolo closed 5 months ago

lukepolo commented 5 months ago

Describe the issue

Reproduction Steps

A bit difficult , but follow upgrade guide from 9 -> 10, i had to change where my "crednetialID" and userHandlel and I am using a custom 'userID' (which does seem to decode correctly after coming back to the client isoBase64URL.toUTF8String(request.body.response.userHandle)

Expected behavior

I expected the same process to work when phone trys create the passkey.

Code Samples + WebAuthn Options and Responses

10.x

advertisements stopped

FIDODebug[17:30:16] Stopping 0 caBLE advertisements

FIDOError[17:30:16] Status 127 from tunnel-5647612D00A05568 is fatal to the request

FIDOError[17:30:16] -> (error reading)

FIDODebug[17:30:16] <- 0x1 (kAuthenticatorMakeCredential) {1: h'EE1BB6F6C3C348A8E72F3AA30DEC9DFDBAF312C9DF382C6ECBFF49513D60982B', 2: {"id": "EXTRACTED", "name": "EXTRACTED"}, 3: {"id": h'30355A68766E7456304B7A', "name": "lpolicinski@EXTRACTED.com"}, 4: [{"alg": -8, "type": "public-key"}, {"alg": -7, "type": "public-key"}, {"alg": -257, "type": "public-key"}], 5: [{"id": h'67FD84C66F9BCA2C0A5C4DD2591B8309DF73EE1A', "type": "public-key"}], 7: {"rk": true, "uv": true}}

9.x

FIDODebug[17:31:31] <- 0x1 (kAuthenticatorMakeCredential) {1: h'9F2A4182B3B5BE05DB086244459B22E6912020F32018AB33AE941157F70487A2', 2: {"id": "EXTRACTED", "name": "EXTRACTED"}, 3: {"id": h'6C706F6C6963696E736B69', "name": "lpolicinski@EXTRACTED.com", "displayName": "lpolicinski@EXTRACTED.com"}, 4: [{"alg": -8, "type": "public-key"}, {"alg": -7, "type": "public-key"}, {"alg": -257, "type": "public-key"}], 7: {"rk": true, "uv": true}}

FIDODebug[17:31:31] The device supports the CTAP2 protocol.

FIDODebug[17:31:31] -> {1: ["FIDO_2_0", "FIDO_2_1"], 2: ["largeBlob"], 3: h'F24A8E70D0D3F82C293732523CC4DE5A', 4: {"rk": true, "uv": true}, 9: ["internal", "hybrid"]}

FIDODebug[17:31:31] tunnel-364A92BCD5875ABE: established v2.1

FIDODebug[17:31:31] Linking information was not received from caBLE device

FIDODebug[17:31:29] Sending CTAP2 AuthenticatorGetInfo request to authenticator.

FIDODebug[17:31:29] tunnel-364A92BCD5875ABE: connecting caBLEv2 tunnel: wss://cable.auth.com/cable/connect/13E806/6E2D9537FBD342C7D9A420058B7718E2

FIDODebug[17:31:29] (1F26B664933C5AD8EEEEDCD30D6E6A22AEAF022D matches QR code)

FIDODebug[17:31:29] Service data: 1F26B664933C5AD8EEEEDCD30D6E6A22AEAF022D

FIDODebug[17:31:29] New caBLE device 09:09:53:25:FB:D3:

registration options:

return await generateRegistrationOptions({
      rpName: "EXTRACTED",
      rpID: this.rpID,
      timeout: 60000,
      userID: user.id,
      userName: user.email,
      attestationType: "direct",
      authenticatorSelection: {
        residentKey: "required",
        userVerification: "required",
        authenticatorAttachment: "cross-platform",
      },
      /**
       * Passing in a user's list of already-registered authenticator IDs here prevents users from
       * registering the same device multiple times. The authenticator will simply throw an error in
       * the browser if it's asked to perform registration when one of these ID's already resides
       * on it.
       */
      excludeCredentials: devices.map((device) => ({
        type: "public-key",
        id: isoBase64URL.toBuffer(device.credentialID),
        transports: device.transports,
      })),
    });

verify

return await verifyRegistrationResponse({
      response: body,
      expectedRPID: this.rpID,
      expectedChallenge: challenge,
      requireUserVerification: true,
      expectedOrigin: this.expectedOrigin,
    });

Dependencies

SimpleWebAuthn Libraries

"@simplewebauthn/server@^10.0.0": version "10.0.0" resolved "https://npm.qumulex.tech/@simplewebauthn%2fserver/-/server-10.0.0.tgz#b7a08b91e60c2811c5858bf047c215a4fa937026" integrity sha512-w5eIoiF7ltg1sgggjY5Tx654j+DBuyEx2B3869jjmPp0xl2Z4BUP4kJ3yJ6DnZIv+ZYYntT3E6nZXNjPOQbrtw==

"@simplewebauthn/browser@^10.0.0": version "10.0.0" resolved "https://npm.qumulex.tech/@simplewebauthn%2fbrowser/-/browser-10.0.0.tgz#8305a57dacb35ab6a71a8b670c699ee7ef1c11c4" integrity sha512-hG0JMZD+LiLUbpQcAjS4d+t4gbprE/dLYop/CkE01ugU/9sKXflxV5s0DRjdz3uNMFecatRfb4ZLG3XvF8m5zg==

Additional context

Safari + Iphone work and able to register and verify etc after saved to DB and what not. Other pass keys such as 1password / chrome profile also work.

I know this isn't perfect information, if needed i can supply more information but i hope someone else is having an issue upgrading

MasterKale commented 5 months ago

I see you're using server@10 but browser@9. I wonder if something during hybrid is falling over because user.id ends up greater than 64 bytes coming out of generateRegistrationOptions(). browser@9 treats user.id as a UTF-8 string, it's browser@10 that will correctly base64url-decode user.id to <= 64 bytes.

Can you provide the output of that call to generateRegistrationOptions()? I think that'll help troubleshoot things further.

lukepolo commented 5 months ago

Blah I’m sorry I copied it from the diff , browser was 10 , I’ll get that output here soon

lukepolo commented 5 months ago

updated to show thati was using the 10 V for the browser, let me see if i can get the output

lukepolo commented 5 months ago
// Registration Options
{
  "challenge": "B6pyIkuOvFU8tor2nDSs6aYyMEdrcws-P4KsVB6m6Ww",
  "rp": {
    "name": "EXTRACTED",
    "id": "EXTRACTED.loca.lt"
  },
  "user": {
    "id": "MHlWTmtiWDAwRnI",
    "name": "lpolicinski@EXTRACTED.com",
    "displayName": ""
  },
  "pubKeyCredParams": [
    {
      "alg": -8,
      "type": "public-key"
    },
    {
      "alg": -7,
      "type": "public-key"
    },
    {
      "alg": -257,
      "type": "public-key"
    }
  ],
  "timeout": 60000,
  "attestation": "direct",
  "excludeCredentials": [],
  "authenticatorSelection": {
    "residentKey": "required",
    "userVerification": "required",
    "requireResidentKey": true
  },
  "extensions": {
    "credProps": true
  }
}
lukepolo commented 5 months ago

i went to https://webauthn.io/ the i noticed it had displayName set. so i added it and now it works. May want to mark that as non optional now?

MasterKale commented 5 months ago

Whoops, this is a case of me getting out ahead of browsers updating to reflect the latest behavior in the draft of WebAuthn L3 (we changed things to make it okay for displayName to be an empty string https://w3c.github.io/webauthn/#dom-publickeycredentialuserentity-displayname.) I must have caused this when I addressed https://github.com/MasterKale/SimpleWebAuthn/issues/536. It seems I'll have to revert that change for now 😞

MasterKale commented 5 months ago

Hello @lukepolo, I confirmed the behavior on my end:

Screenshot 2024-05-31 at 11 18 01 PM

Screenshot 2024-05-31 at 11 18 04 PM

Setting a value for userDisplayName when calling generateRegistrationOptions() fixed it for me too:

Screenshot 2024-05-31 at 11 19 28 PM

I'll work to get a fix out for this over the weekend, thanks for your patience.

Joojo7 commented 5 months ago

Hi @lukepolo thanks for Highlighting this. I also currently have this problem. @MasterKale Thanks for your prompt reaction to this ❤️.

MasterKale commented 5 months ago

Hey all, I asked around last week and ended up reporting this as a Chromium issue here:

https://issues.chromium.org/issues/346835891

I'm still considering updating SimpleWebAuthn, but I also might just document this as a known issue with Chrome and advise RPs to set displayName to a non-empty string till Chrome gets fixed.

MasterKale commented 5 months ago

Alright, I got around to updating the docs with temporary troubleshooting info for this issue:

https://simplewebauthn.dev/docs/packages/browser#startregistration-unexpectedly-errors-out-with-nowallowederror-after-scanning-qr-code

I'll update the guidance once the Chromium bug gets fixed (it'll stick around but at least I can say "this only affects Chrome older than xxx".)

I'm going to close this out for now. Thanks again for your patience.