DEV Community

Amr Abumady
Amr Abumady

Posted on Originally published at clientn.com

User presence vs user verification in passkey sign-in

You tap a sensor, the phone unlocks, or a security key lights up and waits. The site says you are signed in. But what did that touch actually prove, and what did the site learn about you?

Passkeys use WebAuthn. Your device checks something, then signs a message for the site. Two checks get mixed up: user presence and user verification. They are recorded in the same signed message, in different bits, and they tell a site very different things.

Presence: a human touched the key

Presence answers one narrow question: did a person interact with this device at this moment? A touch is a physical act, so it is harder for background software to use the key silently.

But presence does not say which person touched the key. An office key with a button passes a presence check for anyone who presses it. A site that requires only presence learns that a human was involved and nothing more.

Each later signature from the same key needs its own touch. A site that checks only the first one has nothing to say about the rest.

Verification: the device checked the user

When verification happens, the fingerprint, face scan or PIN is checked on the device. The site never receives the fingerprint template, the face data or the PIN. What it receives is a signed assertion that includes the result of the check.

The result says the device's own check passed. It does not name the person, because the passkey was never tied to a name. A site that wants a name has to collect one separately. That is a decision the site makes, not a property of the passkey.

The flags the site reads

Authenticator data ends with one flags byte. The bits that matter here:

  • bit 0 (UP): user presence result
  • bit 2 (UV): a local PIN, password or biometric check passed
  • bit 3 (BE): the passkey can be synced to other devices
  • bit 4 (BS): the passkey is currently synced
  • bit 6 (AT): attested credential data present (registration only)
  • bit 7 (ED): extension data present

The signature covers the whole block, so the site can trust these bits only after it verifies the signature.

Two things catch teams out:

  1. With userVerification: "preferred", an authenticator that cannot verify can still produce a successful sign-in with UV set to 0. With "required", a missing verification fails the sign-in. A site that wants a real answer must read the UV bit itself.
  2. A check at registration does not cover later sign-ins. Sign-in responses are shorter and carry no attested credential data, but UP and UV still record what happened at that moment.

The backup bits describe the credential, not the person. Do not use them as an identifier.

What to do with this

  • Require verification where the action is sensitive, and read the UV bit.
  • Do not describe a button press as strong authentication.
  • Do not treat a registration-time check as permission for everything that follows.

I'm building ClientN, an anonymous sign-in service for websites, so I am not neutral here. The full version, with the flags table, is on our blog: User presence vs user verification in passkey sign-in.

Top comments (0)