Guide Cybersecurity 6 min read

WebAuthn tells your server to ignore the bit that proves two factors

One flag distinguishes a passkey login that used only the device from one that also took a PIN or fingerprint. The spec calls that flag the evidence additional factors were used — and its verification algorithm says to ignore it unless you demanded verification in advance, which the default does not.

Kenji Tanaka
Developer Tools & Cloud Analyst
Published 11 Sep 2026, 4:57 PM (SGT)
Share:
A close-up of a laptop keyboard and trackpad with a small fingerprint reader beside it A close-up of a laptop keyboard and trackpad with a small fingerprint reader beside it Photo by Boris Hamer on Pexels
Advertisement

A passkey login can happen two ways. The device might simply be present — plugged in, or unlocked and nearby. Or the user might have proved something as well: a PIN, a fingerprint, a face. Only the second is two factors.

The signed response carries one bit that distinguishes them, and the WebAuthn specification tells your server to ignore it.

The bit, and what the spec says it is for

The specification is unambiguous that the UV flag is the evidence:

"Although user verification is performed locally on the authenticator and not by the Relying Party, the authenticator indicates if user verification was performed by setting the UV flag in the signed response returned to the Relying Party. The Relying Party can therefore use the UV flag to verify that additional authentication factors were used in a registration or authentication ceremony."

That is the whole mechanism. The server cannot see the fingerprint; it sees the bit.

And then the verification algorithm

The section on verifying an authentication assertion then instructs the server to:

"Determine whether user verification is required for this assertion. User verification SHOULD be required if, and only if, pkOptions.userVerification is set to required. If user verification was determined to be required, verify that the UV bit of the flags in authData is set. Otherwise, ignore the value of the UV flag."

⚠️ So the bit that proves a second factor occurred is to be ignored unless you demanded it in advance. Both statements are in the same document. The first is about what you can learn from the authenticator's response; the second is about what your server is allowed to enforce. But a server following the algorithm literally discards the evidence the other section says it can use.

The default puts you on that path

None of this would matter much if asking for verification were the normal case. It is not the default.

DOMString userVerification = "preferred";

That declaration appears six times across the specification's interfaces. And "preferred" is defined to accept a response without the flag:

"preferred — The Relying Party prefers user verification for the operation if possible, but will not fail the operation if the response does not have the UV flag set."

The default asks for verification but does not require it. The verification algorithm in turn says to ignore the flag if verification was not required. And that flag was the only way to tell the two kinds of login apart. A developer who leaves the default alone builds a system that cannot distinguish one factor from two, with no warning that the distinction has been lost.

What the libraries do

We read the source of four widely used server libraries rather than their documentation. Three follow the specification exactly, which is the point:

LibraryRequestsEnforces on verify?
py_webauthnPREFERREDNo — require_user_verification=False
go-webauthnunset, so the browser applies "preferred"Only if the request said required
Yubico java-webauthn-serveromitted unless the caller sets itOnly if the request equalled REQUIRED
SimpleWebAuthn'preferred'Yes — requireUserVerification=true

⚠️ SimpleWebAuthn is the outlier and it fails in the safe direction: it asks for "preferred" but its verifier demands the flag anyway, so its two halves disagree conservatively. Its own documentation gives the right advice too, telling you to set "discouraged" when the passkey is a second factor behind a password.

Advertisement

The other three are not buggy. They are conformant. That is the uncomfortable part — the silent path is the compliant one, and a reviewer checking a library against the specification will find nothing wrong.

Where this bites

It matters wherever the passkey is meant to be one factor among several, or where a policy distinguishes strong from weak authentication. A step-up prompt before a payment, a session that should be trusted for longer if verification occurred, an audit log claiming multi-factor, a regulator's requirement for two independent factors — all of them need to know which kind of login happened, and all of them are reading a bit the algorithm told the server to discard.

The failure is silent in the worst way. Everything appears to work: users log in, the logs look complete. But the distinction the whole design rests on was dropped at the door.

What this does not mean

It does not mean passkeys are weak or that WebAuthn is badly designed. Even without user verification a passkey is a strong possession factor — bound to the origin, resistant to phishing and replay, and better than the password it replaces. Nothing here is an argument for going back.

It does not mean the specification is wrong either. The instruction to ignore the flag is defensible: acting on a signal you did not request invites servers to build policy on whatever an authenticator happened to do, which varies by device and firmware. Requiring you to ask first is a coherent design.

The default is a decision made by omission. Leaving userVerification unset is an active choice to be unable to tell one factor from two.

What to do about it

Decide what the passkey is for, and set the field accordingly. There is no safe default because the two cases want opposite values.

  • Passkey as the whole login — set userVerification: "required". You need the second factor to be real, and you need to be allowed to check it.
  • Passkey as a second factor behind a password — set "discouraged". Verification is redundant here and requiring it costs the user a prompt for nothing.

⚠️ Then check your verifier separately. Requesting "required" and never enforcing it on the server is the same hole with extra steps, because the request is a hint to the browser and the assertion is what you actually receive. Read the flag on your side.

And if an audit log or a compliance claim says multi-factor, trace it back to a line of code that inspects that bit. If you cannot find one, the claim is describing an intention.

Where this comes from, and what will date it

All four quotations are from the Web Authentication Level 3 specification, fetched here and searched directly — 2.7 MB of text, with "Relying Party" appearing 453 times as a positive control so that the counts below are real. The instruction to ignore the flag appears once; the "preferred" definition once; the sentence naming the flag as evidence of additional factors once; and the IDL default six times. The library behaviours were read from source, not documentation.

⚠️ Two limits. We read four server libraries, chosen for popularity, not a survey — so "three of four" describes those four and not the ecosystem. And we describe what the specification instructs and what the libraries implement; we have not measured how many deployed services leave the default alone, which would need access we do not have.

This dates if the default changes, which would be a substantial and welcome revision, or if the algorithm is amended to let a relying party read the flag without having demanded it. Neither is announced. The durable point is simpler: a signal the server is told to ignore is a signal that is not there, no matter what the wire format carries.

Advertisement
Kenji Tanaka
Developer Tools & Cloud Analyst

Kenji Tanaka covers developer tools, cloud platforms, DevOps, CI/CD, and software supply-chain topics for RECATOOLS.

View author profile → · Editorial policy

About this byline Kenji Tanaka is a RECATOOLS editorial persona for developer tools, cloud, DevOps, and software supply-chain coverage. Articles are produced and reviewed under RECATOOLS editorial supervision.

Corrections policy

Advertisement