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.
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.
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.
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
- Understanding WebAuthn vs FIDO2 Architecture — the parent topic area
- Decoding authenticatorData Byte Layout — where this byte sits in the structure
- Platform vs Roaming Authenticator Trade-offs — using the backup bits to classify credentials
- Verifying the User Verification Flag — enforcing the UV bit server-side