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.55k stars 130 forks source link

Authentication fails with: An attempt was made to use an object that is not, or is no longer, usable #369

Closed karolsojko closed 1 year ago

karolsojko commented 1 year ago

This happens to me only on the production environment and works fine on localhost.

I can successfully register an authenticator but trying to authenticate with it actually gives me this DOMException: Authentication fails with: An attempt was made to use an object that is not, or is no longer, usable. This shows up on Firefox only.

Env: Firefox 110.0.1 (64-bit) Device: YubiKey 5C NFC (Firmware 5.4.3) OS: MacOS 13.2.1

Same error shows up on https://webauthn.io/ when I try to authenticate without the example username. But I can see in the request that the allowedCredentials is an empty array. Whereas in my case I have it filled.

I've checked the FIDO logs on Chrome and the only error I can see is:

[2023/03/10 15:32:43.405627] get_assertion_request_handler.cc:698 Failing assertion request due to status 46 from usb-1050:0407

I'm using: "@simplewebauthn/browser": ^7.1.0 "@simplewebauthn/server": ^7.0.1,

Here's my generate authentication options params: https://github.com/standardnotes/server/blob/main/packages/auth/src/Domain/UseCase/GenerateAuthenticatorAuthenticationOptions/GenerateAuthenticatorAuthenticationOptions.ts#L54-L61

Here's my generate registration options params: https://github.com/standardnotes/server/blob/main/packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.ts#L31-L45

Would appreciate some help with trying to nail down why this only happens in a production environment and does not occure when everything is set up locally with the same parameters.

MasterKale commented 1 year ago
Failing assertion request due to status 46 from usb-1050:0407

I dug into this a bit first, it turns out status 46 (0x2E in hex, it turns out) probably corresponds to the following CTAP status code:

0x2E | CTAP2_ERR_NO_CREDENTIALS | No valid credentials provided.

About being able to recreate on webauthn.io:

...when I try to authenticate without the example username. But I can see in the request that the allowedCredentials is an empty array.

I would expect Firefox to not support an empty allowCredentials because Firefox doesn't have CTAP2 support (yet) and so discoverable credential support is non-existent.

Whereas in my case I have it filled.

It's interesting that your code works on localhost but not production. Can you please provide the following from your production deployment:

  1. The registration options JSON coming out of your GenerateAuthenticatorRegistrationOptions,
  2. The JSON output from however you're calling navigator.credentials.create()
  3. The registration options JSON coming out of your GenerateAuthenticatorAuthenticationOptions,
MasterKale commented 1 year ago

Also when you created this issue, did you see the template with all the sections and instructions? I'm wondering if you deleted all that before writing out your issue, or if GitHub issue templates failed to work for some reason. 🤔

karolsojko commented 1 year ago

Also when you created this issue, did you see the template with all the sections and instructions? I'm wondering if you deleted all that before writing out your issue, or if GitHub issue templates failed to work for some reason. 🤔

Yes, the template was there but I've started describing it in the issue description and once I was done I figured I have everything there to be honest, sorry for not following the template 😅

I would expect Firefox to not support an empty allowCredentials because Firefox doesn't have CTAP2 support (yet) and so discoverable credential support is non-existent.

True. Maybe it's just a coincidence that the error displayed is the same, but figured out it might add to the context. Here's a screenshot of what I can see on FF:

Screenshot 2023-03-10 at 20 09 27

The registration options JSON coming out of your GenerateAuthenticatorRegistrationOptions,

{
  "challenge": "zS5AylmOPj39a91kq_5VNcomQZ1AO8eVmNGhzmP3CwE",
  "rp": {
    "name": "Standard Notes",
    "id": "standardnotes.com"
  },
  "user": {
    "id": "---REDACTED---",
    "name": "---REDACTED---",
    "displayName": "---REDACTED---"
  },
  "pubKeyCredParams": [
    {
      "alg": -8,
      "type": "public-key"
    },
    {
      "alg": -7,
      "type": "public-key"
    },
    {
      "alg": -36,
      "type": "public-key"
    },
    {
      "alg": -37,
      "type": "public-key"
    },
    {
      "alg": -38,
      "type": "public-key"
    },
    {
      "alg": -39,
      "type": "public-key"
    },
    {
      "alg": -257,
      "type": "public-key"
    },
    {
      "alg": -258,
      "type": "public-key"
    },
    {
      "alg": -259,
      "type": "public-key"
    }
  ],
  "timeout": 60000,
  "attestation": "none",
  "excludeCredentials": [],
  "authenticatorSelection": {
    "authenticatorAttachment": "cross-platform"
  },
  "extensions": {
    "credProps": true
  }
}

The JSON output from however you're calling navigator.credentials.create()

{
  "id": "kQcohXxWBVyv28HcvcfGHwDewD2erbYSCjCJFTKj56P1OtGy4-_i9QwTyHtBpwOuO2sJUlu-fICrf5r5xQhgtw",
  "rawId": "kQcohXxWBVyv28HcvcfGHwDewD2erbYSCjCJFTKj56P1OtGy4-_i9QwTyHtBpwOuO2sJUlu-fICrf5r5xQhgtw",
  "response": {
      "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVjE8iUT99vzq1Q0QRoFCndL6Nr0Tv-9eTSfl_pJy9nFQA9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAQJEHKIV8VgVcr9vB3L3Hxh8A3sA9nq22EgowiRUyo-ej9TrRsuPv4vUME8h7QacDrjtrCVJbvnyAq3-a-cUIYLelAQIDJiABIVggtHP2dF5NeRbyML7TV2ZfRCDOYkuASXMSkxfWieUKf0AiWCAR7di77iSEpGhiCs3vZzxExj4T4qic_pKBaxA_dK-ZRw",
      "clientDataJSON": "eyJjaGFsbGVuZ2UiOiJ6UzVBeWxtT1BqMzlhOTFrcV81Vk5jb21RWjFBTzhlVm1OR2h6bVAzQ3dFIiwib3JpZ2luIjoiaHR0cHM6Ly9hcHAuc3RhbmRhcmRub3Rlcy5jb20iLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0"
  },
  "type": "public-key",
  "clientExtensionResults": {}
}

The registration options JSON coming out of your GenerateAuthenticatorAuthenticationOptions,

{
  "challenge": "cJi92eq6xw4mi7sDWwzS1OwMsx4sf6sjTEbOMKSybzA",
  "allowCredentials": [
      {
          "id": "kQcohXxWBVyv28HcvcfGHwDewD2erbYSCjCJFTKj56P1OtGy4-_i9QwTyHtBpwOuO2sJUlu-fICrf5r5xQhgtw",
          "type": "public-key"
      }
  ],
  "timeout": 60000,
  "userVerification": "preferred"
}
MasterKale commented 1 year ago

@karolsojko I can't seem to reproduce this, even with an exact same YubiKey 5C NFC w/Firmware 5.4.3...

Would it be possible to do a screen recording showing registration to failed auth in one take? If not, how about another screenshot like the webauthn.io one you provided, but from standardnotes.com instead?

karolsojko commented 1 year ago

@MasterKale sure.

To add context I've also tried with another key: YubiKey 5 NFC (Firmware 5.4.3) - same thing happens.

Here's the registration flow: 1

And here's the attempt to sign in: 2

karolsojko commented 1 year ago

I'm also wondering if it may have to do something with how the authenticators or challenges data is persisted.

Here's how the authenticator data is persisted: Screenshot 2023-03-13 at 10 39 17

And here's the challenge:

Screenshot 2023-03-13 at 10 39 24

MasterKale commented 1 year ago

@karolsojko The only thing that might be weird is credential_id column is of type blob, but seems to store the value as a base64url string...maybe that's throwing things off? But I feel like that's grasping at straws, since earlier you showed that the credential ID you got back from registration was the same one ending up in allowCredentials during auth.

If your code works fine locally during development but breaks in production...I'm stumped :(

karolsojko commented 1 year ago

I've double checked the credential_id column in MySQL and it's of TEXT type - turns out it's just TablePlus UI that shows TEXT as blob. So that was not it unfortunately. Anyways as this error happens to me on webauthn.io when the allowCredentials is an empty array, my money would be on the credential_id Buffer/Uint8Array/Base64URLString conversion on my end in terms of client<->server<->db communication.

I'll leave this issue open as someone else might encounter the same error. Will close once I find the fix for this. Thanks for the help @MasterKale :)

MasterKale commented 1 year ago

@karolsojko Something you can try is using Firefox Developer Edition 112 on macOS, it enables the new CTAP2 support by default which overhauls the browser's security key support:

https://infosec.exchange/@iamkale/110023439756099109

I wonder if this might resolve your issue even on WebAuthn.io too. If so then you could wait for 112 to roll out to everyone via regular Firefox and the problem will eventually go away 🤔

There hasn't been anything appearing to be an issue with SimpleWebAuthn so far so I'm going to convert this to a discussion. We can continue the conversation over there if anything further develops.