Repository: pretix/pretix

Issue:
WebAuthn 2FA does not work in Safari (TouchID, Solo2)
I'm not quite sure wether this is a Django, Safari, Pretix or whichever bug. I hope we can work out which here, I'm not quite experienced enough with WebAuthn or Django to debug this, I can try things with Safari/macOS though. I can also provide (redacted) logs from the Pretix instance this was tried on.

**Describe the bug**

- Steps with the Solo2 (Hardware security key)
  - Add 2FA device
  - Enter name for device
  - Safari presents modal to add WebAuthn using TouchID or other Options
  - Select "other options" > "security key"
  - Activate/touch Security Key
  - Safari asks for the website to use WebAuthn, the Pretix URL is https://pretix/control/settings/2fa/webauthn/ID/confirm and shows the error "Die Registrierung konnte nicht bestätigt werden. Bitte versuchen Sie es erneut.", allowing the website to use WebAuthn throws up the same flow as as above starting from "Safari presents modal to add WebAuthn using TouchID or other Options" and the whole things loops ad infinitum.
- Steps with TouchID
  - Add 2FA device
  - Enter name for device
  - Safari presents modal to add WebAuthn using TouchID or other Options
  - Use Touch ID
  - The modal "Unable to sign in\n\nThe operation cannot be completed.", the modal is unclosable. After some time the modal closes and Pretix shows "Die Registrierung konnte nicht bestätigt werden. Bitte versuchen Sie es erneut."

**Expected behavior**

I can add WebAuthn devices via Safari as 2FA tokens.


Discussion:
Hmm, I think at least two of our team members actively use WebAuthn with a Yubikey on mac, not sure if they use Safari, though. @wiffbi can you reproduce this?
Regarding the Solo2 key it seems the Keyhandles generated are often longer than 255 chars, which seems to be problematic with a not so little number of services, see https://github.com/solokeys/solo2/discussions/96#discussioncomment-2384191 and for example https://gitlab.com/gitlab-org/gitlab/-/issues/349596 (the Nitro3 uses the same Firmware as Solo2). This should be alleviated with the next Solo2/Nitro3 Firmware, see https://github.com/solokeys/fido-authenticator/pull/11

For reference, the error in the Pretix logs is below and consistent with a "too long something".

```
ERROR 2022-03-24 22:28:48,242 pretix.control.views.user user WebAuthn registration failed
Traceback (most recent call last):
  File "/var/pretix/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.StringDataRightTruncation: value too long for type character varying(255)
```


In the TouchID Case I don't see any error in the Pretix logs, I do see the following in the Safari Console though:

<img width="1121" alt="Bildschirmfoto 2022-03-24 um 22 41 54" src="https://user-images.githubusercontent.com/22835/160015294-71dd1efa-a9b9-415f-b89a-5e4eec8bb2c1.png">

The warning comes on page load, the error comes at the same time the unclosable modal with the message "Unable to sign in\n\nThe operation cannot be completed." closes.
I can confirm that registering Touch-ID as 2FA does not work – in Safari the behaviour fails as described, in Firefox I cannot even select TouchID as a 2FA-device.

According to the error message „User gesture not detected“ and a quick search it seems Safari needs some user interaction (click, touch, whatever) to allow to launch the 2FA-registration (at least for Touch-ID) process with `navigator.credentials.create`. Currently we do this onload.

Strangely using a Yubikey works in Safari. I originally registered the Yubikey through Firefox, which made login attempts in Safari rather cumbersome (you had to click several times each time you log in to reach the Yubikey as 2FA). But once I deleted the Yubikey from my account and set it up as a 2FA-device in pretix using Safari, it worked flawlessly.
From https://webkit.org/blog/11312/meet-face-id-and-touch-id-for-the-web/

> A user gesture is an indicator to signal WebKit that the execution of the current JavaScript context is a direct result of a user interaction, or more precisely from a handler for a user activated event, such as a touchend, click, doubleclick, or keydown event [[3](https://webkit.org/blog/6784/new-video-policies-for-ios/)]. Requiring user gestures for the WebAuthn API means API calls must happen within the above JavaScript context. Normally, the user gesture will not be propagated to any async executors within the context. Since it is popular for websites to fetch a challenge asynchronously from a server right before invoking WebAuthn API, WebKit allows WebAuthn API to accept user gestures propagated through XHR events and the Fetch API.
It’s not the event-handler, but an webkit-bug regarding `attestation=direct` – not sure, if it just affects M1-chips, but some comments seem indicate that.
https://bugs.webkit.org/show_bug.cgi?id=224042

Not sure what to make of this. Wait for it to be fixed? Generally disable attestation=direct or just disable it for M1 based Safari somehow?
A somewhat more future-proof approach would be to try with `attestation=direct` first and if it fails (we might lower the timeout from 60s to something less daunting) we have a retry-button in the error-message, which disables the attestation and tries again? With attestation removed I can add Touch-ID as 2FA-device in Safari.

Resolving patch:
diff --git a/src/pretix/control/views/user.py b/src/pretix/control/views/user.py
--- a/src/pretix/control/views/user.py
+++ b/src/pretix/control/views/user.py
@@ -399,7 +399,8 @@ def get_context_data(self, **kwargs):
             ukey,
             self.request.user.email,
             str(self.request.user),
-            settings.SITE_URL
+            settings.SITE_URL,
+            attestation="none"
         )
         ctx['jsondata'] = json.dumps(make_credential_options.registration_dict)
 


Test patch: