PIN Bypass in Passwordless WebAuthn on and Nextcloud

This blog post is currently not listed publicly!

While implementing FIDO2 and WebAuthn support in our Hardware Security SDK, we found a way to bypass the PIN when logging into

What is FIDO2/WebAuthn?

FIDO2/WebAuthn is an open standard, supported by browsers and driven by tech companies, such as Google and Microsoft. Many websites already adopted the FIDO standard for two-factor authentication. This means, in addition to a password, a login requires an additional FIDO hardware device, such as a YubiKey.

FIDO2/WebAuthn goes one step further and allows users to login without passwords, creating passwordless authentication. The standard is built on Public Key Cryptography and thus eliminates the need to exchange a shared secret between user and website.

With our experience in cryptographic protocols, we evaluated the WebAuthn specification and its threat model. The technology is well thought through, focused on the scenario of authentication and makes the right trade-offs. We have mostly praise for its design.

WebAuthn Passwordless Authentication

Passwordless authentication is yet to be adopted widely. A prominent example that already implements this is Microsoft with its Azure Active Directory.

First, the user adds a security key to her account. The browser will ask the user to set a PIN on her security key. The registration is completed by touching the button on the security key.

Now, on each login, the user needs the PIN and her security key (multi-factor authentication: “something you know and something you have”).

PIN Bypass in Passwordless Authentication

We were able to bypass the PIN when logging into This breakes the assumption of requiring two factors and allows an attacker to log into the victim’s account by using the security key only. He could steal the victim’s USB security key and login without a PIN. Attacking the victim over NFC is even easier by sneaking up on the victim without getting noticed.

Technical Details

On a FIDO2 security key, there is only one PIN, not multiple PINs. Just because a PIN has been set does not mean that all FIDO2 credentials will require a PIN from that point on.

To allow getAssertion calls with or without PIN, the website decides this individually per operation. This is done by setting the optional userVerification property to either required, preferred, or discouraged. Microsoft’s website did not provide a value (see Javascript Snippet in Appendix). In this case, Chrome and other browsers will either ask the user to set a PIN or ask the user to enter the existing PIN (see Chrome’s notice regarding preferred).

It is important to note that userVerification only influences how the browser communicates with the security key. Setting userVerification = "required" provides no cryptographic guarantee by itself that the getAssertion call will always require a PIN. Instead it is up to the Relying Party (the WebAuthn server) to verify that the cryptographic signature in authData has the UV flag set (Step 17 in 7.2. Verifying an Authentication Assertion). This is also noted briefly in Yuriy Ackermann’s blog post (Step 6 under the section “Verifying response”). So to fix this issue, on Microsoft’s implementation of the Relying Party, the UV flag in authData must be checked.

Microsoft’s Response

We reported the issue to Microsoft. They did not consider it a vulnerability, but fixed it:

Thank you for reporting this issue to Microsoft. Our team investigated this, and we agree that userVerification should be required in the described scenarios as part of a defense in depth strategy. We are grateful to the finder for their work in identifying this issue and reporting it to Microsoft.

It is important to note that when a UserPresence check was used in place of a UserVerification check, the resulting token issued reflected correctly that a single-factor authentication occurred. This token would not be usable to access resources protected by strong-auth (MFA) policies or elevate access. This does not lessen the significance of the research but does mitigate the impact of the finding.

We verified that Microsoft indeed fixed the PIN bypass issue.

We do not agree with Microsoft that this was not a vulnerability. If a PIN is requested by the browser during login, users expect that this PIN is actually verified. This was not the case and lead to a false sense of security. Even for us security developers, this was highly unexpected.

Nextcloud 19

Nextcloud introduced WebAuthn passwordless authentication with version 19. In an announcement of Nextcloud’s cooperation with Nitrokey, it is written that this feature provides two-factor authentication:

The server asking for authentication can request verification of multiple factors, so that a configured key requires the user to not just plug it in but also enter a PIN or scan a finger print.

We found that in Nextcloud 19.0.0 and 19.0.1, userVerification is not set and the UV flag is not checked on the server. Thus, even though a FIDO2 key with a PIN is added in a user account, the PIN is not required to log in.

Nextcloud’s Response

We discussed the issue with the Nextcloud team privately. They do not consider this a vulnerability as the feature is supposedly not designed to provide two-factor authentication but only single-factor authentication. They agree that Nitrokey’s blog post is incorrect (it has now been updated) and they will implement userVerification = "discouraged" in their next maintenance release, which will stop browsers from asking for a PIN. For two-factor authentication, they recommend users to enable TOTP or Nextcloud Notifications.

Like with Microsoft’s response, we think that if the user is asked to provide a PIN, the user expects that the PIN is verified. We recommend that Nextcloud documents their threat model and communicates that their passwordless authentication does not provide two-factor protection.

Inconsistencies between Spec and Implementation

WebAuthn specifies that the UV flag should not be checked if the website sets userVerification = 'preferred'. This is unexpected to web developers who observe that Chrome requires a PIN and conclude that their code provides two-factor protection. In fact it only does so when checking the UV flag on the server. We recommend that the FIDO Alliance re-think their position on this matter (see related GitHub discussions) to better match the developers expectations.

Reproducing the Issue

If you like to try how different websites implement WebAuthn, try our SDK example app on Google Play. We added a button to skip the PIN authentication.

Get it on Google Play

We also provide a Tampermonkey script that can be installed in Chrome to override userVerification to always be discouraged.

What about other passwordless WebAuthn logins?

This issue is also present on Yubico’s Playground. Due to time constraints we haven’t yet evaluated other implementations of a FIDO2 Relying Party. A good overview of existing projects can be found on Yuriy Ackermann’s Github repo.


  • 2020-07-20: Added Nextcloud’s response
  • 2020-07-15: Nextcloud 19 tested, included in post and reported to Nextcloud
  • 2020-07-09: Microsoft fixed the issue but does not consider it a vulnerability
  • 2020-06-22: Added more context about userVerification = ‘preferred’
  • 2020-06-18: Finished blog post. Post is not listed publicly.
  • 2020-06-18: Reported to Microsoft as VULN-026995


In our tests, the useNewDefaults branch is not executed, thus userVerification is not set.

exports.getAssertion = function (serverChallenge, serverAllowList, rpId)
    var allowListParam = [];

    if (serverAllowList)
        allowListParam =
            function (credentialId)
                return { type: "public-key",
                    id: TypeConverter.base64UrlStringToArrayBuffer(credentialId) };

    var publicKeyCredentialRequestOptions =
        challenge: TypeConverter.stringToArrayBuffer(serverChallenge),
        timeout: FidoConstants.Timeout,
        rpId: rpId,
        allowCredentials: allowListParam

    if (useNewDefaults)
        publicKeyCredentialRequestOptions.userVerification = "required";

    return n.credentials.get({ publicKey: publicKeyCredentialRequestOptions });
  1. Install Tampermonkey
  2. Add this script:
// ==UserScript==
// @name         Skip WebAuthn PIN verification
// @namespace
// @version      1.0
// @description  Discourage WebAuthn userVerification
// @author       Vincent Breitmoser
// @match        https://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const origGet = navigator.credentials.get.bind(navigator.credentials);
    navigator.credentials.get = function(arg) { console.log(arg); arg.publicKey.userVerification = "discouraged"; return origGet(arg); }
Dr. Dominik Schürmann

Before founding COTECH, Dominik Schürmann was a researcher at the Technische Universität Braunschweig and worked on network security and cryptographic protocols. Yet, he did not lose sight of the usability aspects of IT security and conducted several user studies.

Vincent Breitmoser

Vincent Breitmoser is a developer and consultant specialized in Android security and encryption protocols. He is one of the main developers of K-9 Mail, the leading open source email app for Android, and participated in writing the Autocrypt specification.

Passwordless Password Manager

COTECH is also developing the next-generation password manager. Login easily with your smartphone, without entering a master password!

Sign up for our beta, starting next month: