bartnv / twofactor_webauthn

Roundcube plugin for FIDO2/WebAuthn 2-factor authentication
GNU General Public License v3.0
30 stars 7 forks source link

Unable to register a security key #15

Closed bovender closed 2 years ago

bovender commented 2 years ago

I am unable to register a security key. When I click on "Add security key", nothing happens. The JavaScript console shows "oink", which originates from this line: https://github.com/bartnv/twofactor_webauthn/blob/78d8f91033c50395698bb1e181a3104f60902b0b/twofactor_webauthn.js#L29

However, evidently the subsequent call to webauthnRegister is not successful.

Could it be that this is because I am running Roundcube in a Docker container behind a reverse proxy? I have enabled SSL for the communication between reverse proxy and the Docker container (which required me to a2enmod ssl, apt-get install ssl-cert, and a2ensite default-ssl), but that did not help.

I have successfully used webauthn for a number of self-hosted apps which are sitting behind the same reverse proxy, so I believe something is wrong with my specific Roundcube setup.

Is this a bug with the plugin?

bartnv commented 2 years ago

I'm not sure what's going wrong there. The webauthn 'library' I used isn't very clear about the possible failure modes and I didn't dive into the webauthn spec to figure them out, which is why error handling is lacking atm. Could you please apply the following patch to get some more logging out of it and try again?

diff --git a/twofactor_webauthn.js b/twofactor_webauthn.js
index 73e0188..9672e42 100644
--- a/twofactor_webauthn.js
+++ b/twofactor_webauthn.js
@@ -28,11 +28,12 @@ function twofactor_webauthn_save() {
 function twofactor_webauthn_challenge(data) {
   console.log('oink');
   if (data.mode == 'register') {
+    console.log(data);
     webauthnRegister(data.challenge, function(success, info) {
       if (success) {
         rcmail.http_post('plugin.twofactor_webauthn_register', { response: info });
       }
-      else { /* do something already! */ }
+      else { console.log(info); }
     });
   }
   else if (data.mode == 'test') {

Hopefully the JS console will then give us some more information.

bovender commented 2 years ago

Thank you so much for trying to help with this!

data:

{
  "mode": "register",
  "challenge": ... see below ...
}
{
  "publicKey": {
    "challenge": [
      116,
      71,
      165,
      243,
      101,
      100,
      63,
      21,
      89,
      194,
      115,
      168,
      51,
      82,
      210,
      177
    ],
    "user": {
      "displayName": "RoundCube",
      "name": "RoundCube",
      "id": [
        49
      ]
    },
    "rp": {
      "id": "localhost:8085",
      "name": "localhost:8085"
    },
    "pubKeyCredParams": [
      {
        "alg": -7,
        "type": "public-key"
      },
      {
        "alg": -257,
        "type": "public-key"
      }
    ],
    "authenticatorSelection": {
      "authenticatorAttachment": "cross-platform",
      "requireResidentKey": false,
      "userVerification": "discouraged"
    },
    "attestation": null,
    "timeout": 60000,
    "excludeCredentials": [],
    "extensions": {
      "exts": true
    }
  },
  "b64challenge": "dEel82VkPxVZwnOoM1LSsQ"
}

info:

SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current domain.

I wonder if it has something to do with this being behind a reverse proxy. I mean, the "rp"'s "id" above is "localhost:8085", but this, of course, is not the outside domain?

bovender commented 2 years ago

OK, now I added

ProxyPreserveHost on

to my Apache config. Now I am being prompted to insert the security key, but when I touch the key's button, I get logged out of Roundcube immediately.

Will investigate further...

bovender commented 2 years ago

OK, finally got it to work. Weirdly, inbetween my trying to fix it, the SSL connection between the reverse proxy and the docker container failed with handshake errors, but I succeeded by re-simplifying the setup. In the end, it turned out that the immediate logout was caused by the twofactor_gauthenticator plugin -- evidently the two don't agree with one another.

So, to summarize: Getting webauthn to work in a setup with an Apache reverse proxy and a Roundcube docker container:

  1. Use unencrypted connection between reverse proxy and container (provided both live on the same host, and the container does not expose any ports to the outside world).
  2. Ensure you have ProxyPreserveHost on in your Apache site config.
  3. Do not enable the twofactor_webauth and twofactor_gauthenticator plugins together.

It would be great though if I could have the choice at run-time whether I use my security key or a TOTP generated by an app.

Thanks again for your help.

bartnv commented 2 years ago

Good to hear you've solved your problem. And thanks for sharing your insights. I created this plugin by modifying the twofactor_gauthenticator one, so it's certainly possible that they interfere with each other. It was my intention to also add TOTP functionality to this one because it makes sense to offer various 2FA mechanisms as a fallback. But unfortunately time is limited and I have some projects with more priority. But I hope to revisit this in the future.