Understanding the WebAuthn Flags Byte

Byte 32 of authenticatorData is a single bitfield carrying six defined flags, and reading it by eye is a reliable source of mistakes. This page covers what each bit asserts, the difference between presence and verification that most policies blur, and how the two backup bits let a relying party decide whether an account is one lost device away from a recovery event. It sits under Understanding WebAuthn vs FIDO2 Architecture; the surrounding structure is described in decoding authenticatorData.


Bit Reference

Bit Name Set means Where it matters
0 UP user present every ceremony; effectively always set
2 UV user verified step-up policy, sensitive actions
3 BE backup eligible whether the credential can be synced
4 BS backup state whether it currently is synced
6 AT attested credential data follows distinguishes registration from assertion
7 ED extension data follows parsing the tail of the structure

Bits 1 and 5 are reserved. A response with either set is not something to accommodate; it is something to reject.

The six bits that matter Byte 32 is a bitfield, and each bit answers a question your policy may depend on. One byte, six meanings, two of them structural UP — user present somebody interacted with the authenticator; the weakest possible claim UV — user verified the authenticator satisfied itself this is the enrolled person BE — backup eligible the credential may be synced to other devices BS — backup state it currently is synced and would survive losing this device AT — attested data attested credential data follows; this is a registration ED — extension data extension output follows the structure

Root Cause Analysis

1. Checking UP when the policy meant UV. The presence bit is set in essentially every ceremony, so a check against it always passes and provides no assurance. A route that intends to require a verified human and tests the presence bit is not enforcing anything.

2. Assuming the request determines the result. Setting userVerification to required is a request to the client, not a guarantee. Some authenticators cannot verify a user; some clients will proceed anyway. The only authority is the bit in the response.

3. Ignoring the backup bits entirely. They are the newest addition and the most commonly unread. Without them there is no way to distinguish a credential that will survive a lost phone from one that will not, and a product cannot tell which accounts genuinely need a second passkey.

4. Treating a change in the backup state as an anomaly. The bits move for ordinary reasons — a user enabling sync, restoring a device, switching platform accounts. Alerting on the change produces noise; recording it produces useful history.

Presence is not verification A tap proves somebody was there; verification proves the authenticator recognised the enrolled user. The distinction that most policies get wrong UP only a touch, a button, a tap no biometric or PIN involved anyone holding the device passes not a factor in any useful sense UV set biometric, PIN or device unlock the authenticator made a judgement a second factor in substance required for sensitive actions Requesting user verification does not guarantee it happened — check the bit in the response.

Step-by-Step Resolution

Step 1 — decode into named booleans

Convert the byte into a small structure with named fields once, at the parse boundary. Every subsequent check then reads as a question about the ceremony rather than as a bitmask operation, and the class of “wrong bit” bug disappears.

Step 2 — reject reserved bits

If either reserved bit is set, the response is malformed. Refuse it rather than masking it away.

Step 3 — enforce UV where policy requires it

Check the verification bit explicitly on every route that needs it, using the decoded value rather than the request you made. Record the result in the session so downstream decisions do not have to re-derive it.

Step 4 — persist the backup bits on every assertion

Write them back when they change, with a timestamp. This is a conditional update on a path that is already writing the counter, and it gives you both the current state for policy and a history for support.

Step 5 — branch on AT before parsing further

The attested-data bit is what tells you whether the rest of the structure exists. Treat it as the guard for the entire variable-length walk.

Reading the backup bits together The two backup bits form four states, three of which are meaningful and one of which should never appear. Four combinations, one of them impossible BE clear, BS clear — device-bound; lost with the hardware BE set, BS clear — eligible but not yet synced; fragile today BE set, BS set — synced; survives losing this device BE clear, BS set — contradictory; treat as a malformed response These bits are the cleanest signal available for deciding whether an account needs a second credential.

Verification and Testing

Fixtures are the only reliable way to test this. Capture one assertion with verification performed and one without, and assert that your decoder distinguishes them — a synthetic byte you constructed will always agree with the code that constructed it.

Assert the reserved-bit rejection with a deliberately malformed fixture. It is a two-line check that never fires in production and prevents a whole class of future confusion.

For the backup bits, the most valuable test is a state transition: run the same credential through two assertions with different backup states and assert that your persistence layer records the change rather than ignoring it because the credential id already exists.


Pitfalls

1. Masking with the wrong bit position. Name the bits once; never write a hexadecimal mask inline at a call site.

2. Trusting the request over the response. The request is a preference. The bit is the fact.

3. Treating an unset BS as a problem. It simply means the credential is not currently backed up, which is normal and is the moment to suggest a second one.


Frequently Asked Questions

Is the user-presence bit ever clear?

Essentially never in practice. The specification allows it to be clear only in narrow circumstances that ordinary web ceremonies do not produce, so treat a clear presence bit as a malformed response rather than as a state to accommodate.

Does user verification mean biometric?

Not necessarily. It means the authenticator satisfied its own user-verification method, which may be a fingerprint, a face scan, a PIN or a device unlock. The protocol does not tell you which, and it should not — the assurance is that a verification occurred, not which modality performed it.

Can I require the backup-state bit to be set?

You can check it, but requiring it excludes every hardware security key and every device-bound platform credential, which is rarely what a policy intends. The productive use is the opposite: when the bit is clear, prompt the user to add a second credential, because the one they have will not survive losing the device.

What happens to these bits during registration?

They appear in exactly the same place, so a registration response tells you at enrolment time whether the credential is device-bound or synced. Recording them then gives you a starting state to compare later assertions against.

Should the flags be shown anywhere in a product interface?

Only in translated form. “This passkey is saved to your account and works on your other devices” is a sentence derived from the backup bits that a user can act on. The bit names themselves belong in logs.


How should the flags interact with a risk engine?

As inputs rather than as verdicts. The verification bit is the strongest of them and is a genuine assurance signal; the backup bits describe the credential’s durability rather than the trustworthiness of this particular ceremony. Feeding all three into a risk score is reasonable, provided the weighting reflects that difference — a synced credential is not more suspicious than a device-bound one, it is simply less likely to be lost.

What is the cheapest way to audit an existing implementation?

Search the codebase for hexadecimal masks applied to a byte named something like flags. Every occurrence is a place where the bit position is asserted inline, and in most implementations at least one of them is checking presence where the surrounding comment says verification. Replacing them all with a single named decode is a small change that removes a class of defect which is otherwise invisible in review, because a wrong mask and a right mask look equally plausible on the page.

Related