A relying party sees a WebAuthn assertion with a different backup-state bit than the value stored after an earlier ceremony. The tempting response is to treat the change as evidence that a credential was copied, synchronization was enabled or disabled, or the user deliberately changed something. That interpretation is too strong.
WebAuthn Level 3 makes a narrower distinction. Backup Eligibility (BE) is a property chosen for the public-key credential source when it is created, and that property is permanent for that credential source. Backup State (BS) reports the credential's current backup state and may change over time. The two values therefore have different lifecycles and should not be collapsed into one “passkey is synced” field.
As of September 22, 2026, WebAuthn Level 3 is a W3C Recommendation dated August 25, 2026. Its authenticator-data layout assigns bit 3 to BE and bit 4 to BS, and its relying-party algorithms explicitly address their validation and storage.
The operational question for this article is therefore precise: before an RP changes authentication policy because a backup flag changed, can it prove that it parsed the flags correctly, preserved immutable backup eligibility, accepted only valid BE/BS combinations, distinguished a legitimate BS transition from a BE contradiction, and avoided state mutation when the ceremony failed?
The lab below is intentionally synthetic. It does not use, capture, import, or modify any real user's credential. It defines recorded authenticator-data fixtures and test-only state transitions so an engineering team can validate policy branches without turning assumptions about browser or platform behavior into WebAuthn requirements.
Scope and acceptance target
This article is about the two WebAuthn authenticator-data bits that describe credential backup characteristics and the relying-party decisions that follow from them. It deliberately does not become a generic passkey introduction, a platform comparison, or a survey of passwordless authentication.
That boundary matters because authenticator data is a protocol artifact. WebAuthn defines it as a byte array of at least 37 bytes: a 32-byte RP ID hash, a one-byte flags field, and a four-byte signature counter, followed by optional data. BE occupies bit 3 and BS bit 4.
The specification gives the key acceptance invariant: BE is fixed when the credential source is created and MUST NOT change, while BS may change according to the credential's current state. The combination BE=0, BS=1 is not allowed.
Scope item | In this article | Acceptance evidence |
Decode BE and BS from authenticator data | Yes | Raw flag byte plus decoded Boolean values |
Enforce valid BE/BS combinations | Yes | 00, 10, 11 accepted structurally; 01 rejected |
Preserve credential backup eligibility | Yes | Stored registration BE equals every later accepted assertion BE |
Evaluate BS transitions | Yes | Explicit 0→1, 1→0, unchanged-state branches |
Browser or operating-system comparison | No | No platform-specific conclusion required |
Infer which backup/sync mechanism was used | No | No such inference enters policy |
Infer user intent from BS | No | Policy records an observation, not an intent claim |
Real credential testing | No | Synthetic fixtures only |
This article deliberately does not repeat generic authentication selection, OAuth/JWT discussion, rate limiting, general API hardening, or broad deployment guidance already better suited to Building Secure and Scalable APIs. Its acceptance target is much narrower: an engineer should leave with testable RP rules for BE permanence, BS variability, invalid combinations, state persistence, and rollback.
Documented behavior versus engineering inference
A policy review becomes unreliable when normative WebAuthn requirements, implementation choices, experiments, and observations are written as though they have equal authority. They do not.
Documented behavior comes directly from the WebAuthn specification. The generating authenticator determines backup eligibility when the credential source is created; eligibility is permanent for that source. The current managing authenticator determines backup state, and that state may change over time. For a multi-device credential, the authenticator managing a later authentication can differ from the authenticator that originally generated the credential.
The specification also says backup can occur through multiple mechanisms, including peer-to-peer synchronization, cloud synchronization, local-network synchronization, or manual import/export. Therefore BS=1 does not tell an RP which mechanism was involved.
Evidence class | Example in this article | How to treat it |
Documented behavior | BE is permanent; BS may change | Requirement or defined semantic from W3C |
Engineering inference | Arrival-order writes can obscure the order in which different assertions were generated | Design risk derived from protocol/state behavior |
Proposed experiment | Feed synthetic 0x09, 0x19, 0x11 flag bytes into the policy function | Test to run; not an observed result |
Actual observation | The cited WebAuthn Level 3 Recommendation is dated August 25, 2026 | Research fact checked for this article |
A particularly important engineering inference concerns intent. WebAuthn says BS can transition from 1 to 0 because of user actions or errors associated with backup service behavior. It does not provide an intent bit. An RP may therefore react to the resilience consequence of BS=0, but should not label the event “user disabled sync” unless it has independent evidence for that conclusion.
Likewise, BS=1 should not be translated into “we proved this private key was copied to device X.” WebAuthn defines the observable state, while the specification explicitly allows different backup mechanisms. The defensible record is “authenticator reported BS=1 in this validated ceremony,” not an invented history of how or why the state arose.
That distinction should survive into incident notes, dashboards, and customer-facing text. It prevents an authenticator-provided status signal from acquiring more forensic certainty than the protocol gives it.
Lab fixture and version matrix
The primary lab should operate below the browser UX layer. A minimal authentication authenticatorData value is enough to test the flag decoder: 32 bytes of rpIdHash, one byte of flags, and four bytes of signCount. The W3C definition puts the flags byte at offset 32.
For assertions with UP set and UV omitted, four useful synthetic flag bytes are:
Fixture | Flags byte | BE | BS | Intended result |
single_device | 0x01 | 0 | 0 | Structurally valid |
eligible_not_backed_up | 0x09 | 1 | 0 | Structurally valid |
eligible_backed_up | 0x19 | 1 | 1 | Structurally valid |
invalid_bs_without_be | 0x11 | 0 | 1 | Reject |
Those values follow directly from UP=0x01, BE=0x08, and BS=0x10. WebAuthn defines 00, 10, and 11 as valid BE/BS combinations and explicitly disallows 01.
A tiny fixture generator can keep this test deterministic:
import hashlib
UP = 0x01
BE = 0x08
BS = 0x10
RP_ID = "example.test"
RP_ID_HASH = hashlib.sha256(RP_ID.encode("utf-8")).digest()
def authenticator_data(flags: int, sign_count: int = 1) -> bytes:
if not 0 <= flags <= 0xFF:
raise ValueError("flags must fit in one byte")
if not 0 <= sign_count <= 0xFFFFFFFF:
raise ValueError("sign_count must fit in four bytes")
return RP_ID_HASH + bytes([flags]) + sign_count.to_bytes(4, "big")
FIXTURES = {
"single_device": authenticator_data(UP),
"eligible_not_backed_up": authenticator_data(UP | BE),
"eligible_backed_up": authenticator_data(UP | BE | BS),
"invalid_bs_without_be": authenticator_data(UP | BS),
}
This code is a proposed fixture, not an executed test. It tests byte parsing and policy branching; it does not, by itself, prove that a complete WebAuthn assertion passed challenge, origin, RP ID hash, signature, UV, extension, or counter verification.
Version and environment matrix
Record versions even when the lab is intentionally small. As of September 22, 2026, Python.org lists CPython 3.14.7, released August 5, 2026, while Python 3.15 is still listed as a prerelease with October 1, 2026 planned.
Component | Proposed lab value | Why it is recorded |
WebAuthn normative reference | W3C WebAuthn Level 3 Recommendation, August 25, 2026 | Locks flag semantics to a published specification |
Fixture interpreter | CPython 3.14.7 | Makes byte-decoder reruns reproducible |
RP ID | example.test | Synthetic/disposable namespace |
Real user credentials | None | Required isolation boundary |
Browser | None required for primary fixture test | Prevents browser behavior from becoming protocol evidence |
OS dependency | None intended | Parser exercises raw bytes only |
Fixture schema | backup-flags-v1 | Lets evidence identify the test-data format |
Optional WebDriver layer | Version recorded at execution time | Necessary only if virtual-authenticator testing is added |
WebAuthn Level 3 also defines a WebDriver “Set Credential Properties” command that can alter backupEligibility and backupState on a virtual authenticator credential. The specification explicitly notes that backup eligibility is normally permanent but permits changing it through this command for testing and debugging. That makes a virtual authenticator appropriate for a secondary deliberate-failure lab, without asserting that a real conforming credential changes BE.
Baseline state and evidence model
The baseline needs two different stored values because WebAuthn gives them two different meanings. A credential record is recommended to contain backupEligible, defined as the BE value when the credential source was created, and backupState, defined as the latest BS value observed in authenticator data from a ceremony using that source.
At registration, the RP algorithm first rejects BS=1 when BE=0. It can then evaluate the flags for local policy, and the credential record created by the registration procedure stores the BE and BS values from that registration's authenticator data.
The baseline state model should therefore look like this:
Stored BE | Stored BS | Meaning at baseline | Permitted future observation |
0 | 0 | Single-device credential | BE must remain 0; BS must remain 0 |
1 | 0 | Backup-eligible, not currently backed up | BE stays 1; BS may remain 0 or become 1 |
1 | 1 | Backup-eligible and currently backed up | BE stays 1; BS may remain 1 or become 0 |
0 | 1 | Invalid | Never create or accept this baseline |
The useful conceptual state machine is therefore not four freely interchangeable states. It is:
BE=0, BS=0
│
└── BE cannot become 1 for this credential source
BE=1, BS=0 <──────────────> BE=1, BS=1
BS may change
BE=0, BS=1 = invalid
That model prevents a common schema mistake: updating both backupEligible and backupState on every successful login. The W3C credential-record definition treats backupEligible as the creation-time BE value, while backupState tracks the latest BS observation.
Engineering inference: make that semantic distinction visible in the database API. A field called backup_flags that is blindly replaced after every assertion makes it easier to overwrite immutable eligibility accidentally. Separate columns such as backup_eligible_at_registration and latest_backup_state communicate the lifecycle better, even though those exact database names are an application design choice rather than W3C terminology.
The baseline evidence package should contain the synthetic credential identifier, initial BE, initial BS, raw flags byte, fixture version, policy version, and expected decision. It should not contain a real credential ID, private key, or unrelated personal data.
Normal-path validation
The normal path begins by refusing to treat BE/BS as isolated business metadata. They are part of authenticator data, and an authentication assertion's signature is verified over the authenticator data concatenated with the hash of the client data.
WebAuthn's RP authentication algorithm first checks the normal ceremony conditions, including credential ownership, challenge, origin, RP ID hash, user presence, and any required user verification. It validates that BS is not set when BE is clear. When backup state participates in RP business logic or policy, it compares current BE with the credential record's stored backup eligibility and then applies RP policy. Signature verification follows in the normative algorithm.
For application architecture, the important persistence rule appears later: state updates occur only after the relevant verification steps, and if an RP performs additional security checks beyond the WebAuthn procedure, the specification says those state updates should be deferred until those checks succeed.
Incoming observation | Stored value | Normal-path decision |
BE=0, BS=0 | BE=0, BS=0 | Continue after other ceremony checks |
BE=1, BS=0 | BE=1, BS=0 | Continue; state unchanged |
BE=1, BS=1 | BE=1, BS=0 | Continue if local BS policy allows; stage 0→1 update |
BE=1, BS=0 | BE=1, BS=1 | Continue if local BS policy allows; stage 1→0 update |
BE=0, BS=1 | Any | Reject invalid combination |
BE differs from stored BE | Any | Fail eligibility verification when backup-state policy is in use |
A useful policy-function shape is:
parse authenticatorData
validate BE/BS structural combination
compare current BE with registration BE
classify current BS versus stored BS
apply explicit RP policy
complete cryptographic and additional security checks
commit latest BS only after successful completion
The exact execution graph can vary by WebAuthn library, but two semantics must survive the abstraction: a changed BS is possible; a changed BE for the same credential source contradicts the credential's stored eligibility.
A 0→1 BS transition means the authenticator is reporting that the credential is currently backed up. A 1→0 transition means the authenticator is reporting that it is no longer backed up and therefore is no longer protected from single-device loss. W3C recommends that an RP encountering the latter guide the user through validating other authentication factors and, where necessary, adding another credential.
None of those branches requires claiming that a particular cloud, device, export operation, or user choice caused the transition.
Boundary-condition experiment
The boundary lab should test both kinds of failure that production code often conflates: an impossible combination inside one authenticator-data value and an eligibility contradiction across ceremonies.
Use only synthetic credential records. A proposed baseline can be stored_BE=1, stored_BS=0. Feed the policy layer the following fixtures and record the decision without mutating the baseline until the full synthetic ceremony is considered successful.
Test | Stored BE/BS | Incoming BE/BS | Expected policy result |
Stable eligible state | 1/0 | 1/0 | Accept branch |
Legitimate state change | 1/0 | 1/1 | Accept branch; stage BS update |
Return from backed up | 1/1 | 1/0 | Apply 1→0 resilience policy |
Impossible combination | 1/0 | 0/1 | Reject before state update |
Eligibility contradiction | 1/0 | 0/0 | Reject BE mismatch when policy comparison applies |
Single-device baseline | 0/0 | 0/0 | Accept branch |
Impossible single-device backup | 0/0 | 0/1 | Reject |
WebAuthn directly supports the structural expectations: 0/1 is prohibited, BE must not change after credential creation, and RP verification compares current BE with stored eligibility when credential backup state is used in policy.
Deliberately failing boundary test
The most valuable deliberately failing test starts with a synthetic credential registered as BE=1, BS=0 and later supplies BE=0, BS=0 for that same credential identity.
This is not intended to simulate ordinary conforming behavior. It intentionally violates the BE permanence rule so the test can demonstrate that the RP refuses to silently “learn” a new eligibility value. WebAuthn's WebDriver extension is specifically capable of changing a virtual credential's backupEligibility for testing and debugging even though the property is normally permanent.
For a byte-only unit test, the proposed expected transcript is:
stored: BE=1 BS=0
incoming: flags=0x01 -> BE=0 BS=0
result: REJECT_BACKUP_ELIGIBILITY_MISMATCH
write: none
For the independent malformed-state test:
incoming: flags=0x11 -> BE=0 BS=1
result: REJECT_INVALID_BACKUP_FLAG_COMBINATION
write: none
These are expected outputs of a proposed experiment, not observed outputs from an executed lab.
An especially dangerous implementation would instead produce:
stored before: BE=1 BS=0
incoming: BE=0 BS=0
stored after: BE=0 BS=0
result: success
That would convert an invariant violation into apparently valid state. The RP would erase the very evidence needed to distinguish credential eligibility from current backup state, contrary to the Level 3 model in which stored backupEligible represents the creation-time BE value.
Recovery and rollback behavior
A failure is not fully tested merely because the next request succeeds.
Suppose the deliberately failing request reaches policy evaluation and is rejected for a BE mismatch. A subsequent request using the original valid fixture may return green. That green rerun proves only that the later request can succeed. It does not prove that the failed attempt left the database, cache, session, risk record, audit stream, or downstream account state untouched.
The WebAuthn RP algorithm reinforces the underlying principle by placing credential-state updates after verification and advising RPs to defer them until additional security checks have completed successfully.
Recovery artifact | State before failed attempt | Required state after failure |
Stored backupEligible | 1 | 1 |
Stored backupState | 0 | 0 |
Authentication result | Not applicable | Failed |
Pending policy mutation | None | None |
Session elevation | None | None |
User-facing recovery workflow | Not triggered unless policy explicitly calls for it | Deterministic |
Audit event | No event yet | Failure event retained |
Next valid request | Untested | May succeed, but does not erase failure evidence |
A rollback test should therefore take a before-snapshot, issue the deliberately failing synthetic request, assert the rejection, and take an after-snapshot. Only then should it issue the clean rerun.
Engineering inference: transactional boundaries matter more than endpoint status codes here. If the RP updates backupState before signature verification, or writes an account-security change before a later policy check fails, a 401 or equivalent response may hide a partially committed mutation. The specification's deferral guidance is a strong reason to make persistence an explicit end-of-ceremony operation.
The same rule applies to rollback during deployment. Disabling the new policy branch should not mean deleting recorded observations. Roll back the decision logic while retaining enough versioned evidence to determine which rule evaluated each ceremony.
Concurrency, lifecycle, or version interaction
Backup state is mutable, but WebAuthn does not give an RP a globally ordered synchronization history for it.
For a multi-device credential, the generating authenticator may differ from the managing authenticator involved in a later authentication ceremony. The RP's credential-record abstraction describes backupState as the latest BS value from a ceremony using that credential source.
That creates an application-level concurrency problem. If two valid assertions are processed concurrently, “latest” can accidentally mean “last database commit” rather than “a trustworthy global chronology of backup operations.” WebAuthn's discussion of signature counters separately acknowledges that an RP can process assertion responses in a different order from the order in which authenticators generated them.
Interaction | Documented fact | Engineering treatment |
Multi-device credential used by different managing authenticators | Permitted | Do not assume one device supplies every observation |
BS differs between accepted ceremonies | BS may change | Record transition as an observation |
Two requests race | Processing order can differ from generation order | Avoid claiming server commit order proves real-world transition order |
BE differs | BE must be permanent | Treat as verification/invariant failure, not a normal race |
Legacy implementation built around Level 2 | Level 2 Recommendation predates Level 3 BE/BS model | Audit parser/schema before enabling Level 3 policy |
WebDriver changes BE | Allowed for testing/debugging | Never interpret test mutation as normal credential behavior |
WebAuthn Level 2 was published as a W3C Recommendation on April 8, 2021. Searches within that published Level 2 document do not contain the Level 3 “Backup Eligibility” or “backup state” terminology, while the August 25, 2026 Level 3 Recommendation defines BE/BS and the corresponding RP behavior.
That version distinction matters when upgrading an RP or library. An old schema may have no columns for backupEligible and backupState; an old parser may regard the formerly reserved flag positions differently; and an application may have policy assumptions written before these Level 3 semantics existed. Those are migration questions to test, not reasons to reinterpret the current specification.
A safe version-interaction test feeds the same recorded Level 3 fixtures through both the existing parser and the proposed parser, compares decoded flags, and blocks rollout if the two disagree unexpectedly. For broader API-versioning discipline, the same principle of making compatibility behavior explicit appears in Refonte Learning's API developer engineering guide.
Observability and evidence retention
The observability goal is not to collect everything. It is to preserve enough evidence to reproduce the RP's decision without turning authentication telemetry into an unnecessary archive of credential material.
A useful event records the policy inputs, policy version, decision, and mutation outcome. It should distinguish raw protocol observation from interpretation: observed_be=true is different from reason="credential copied", because the latter claim is not supplied by WebAuthn.
Evidence field | Retain? | Purpose |
Ceremony correlation ID | Yes | Connect evaluation and persistence events |
Synthetic fixture ID in lab | Yes | Make tests reproducible |
Policy version | Yes | Explain which decision branch ran |
Observed BE | Yes | Check immutable eligibility |
Observed BS | Yes | Classify current state/transition |
Previous stored BE/BS | Yes, where appropriate | Reconstruct comparison |
Decision code | Yes | Distinguish malformed combination, BE mismatch, local BS policy |
State-write outcome | Yes | Prove whether mutation occurred |
Full private credential material | No | Not needed for this policy evidence |
Assumed sync provider/device | No, unless independently known | BE/BS does not establish it |
Assumed user intent | No | BS does not encode intent |
Authenticator data derives its trust from the RP's assessment of the authenticator, and the data participates in the signed assertion. That makes it meaningful protocol evidence after verification, but it still does not expand the semantic meaning of BE or BS beyond what WebAuthn defines.
Data-retention design should also be reviewed through a privacy and minimization lens rather than assuming “security logging” justifies indefinite collection. Refonte's broader data-protection discussion across roles is a useful adjacent topic, while the WebAuthn-specific event here should stay focused on the minimum evidence necessary to reproduce the decision.
Evidence that must survive a rerun
A rerun must not overwrite the evidence for the request that failed. Give each attempt its own immutable correlation identifier and retain its result independently.
Artifact | Failed attempt | Clean rerun |
Request/correlation ID | A | B |
Fixture | BE mismatch | Valid baseline |
Policy result | Reject | Expected success |
Stored state before | Captured | Captured |
Stored state after | Proved unchanged | Updated only if legitimate |
Policy version | Recorded | Recorded |
Audit entry | Retained | Separate entry |
Evidence relationship | Original failure | Does not replace or “heal” A |
This distinction is crucial to the deliberately failing test. A green B says nothing about whether A temporarily changed a cache, inserted a database row, generated a session, queued an account action, or emitted the wrong audit classification.
The acceptance evidence must instead show that A produced the intended failure and that all prohibited side effects remained absent. Only after that proof does the green rerun demonstrate recovery of the normal path.
Negative tests and false confidence
Positive tests are easy to overvalue in stateful authentication logic. Three valid fixtures can prove that three happy branches work while leaving the dangerous invariant failures untouched.
The strongest suite attacks each assumption separately. It also keeps cryptographic verification and policy interpretation conceptually separate: a byte-parser test can prove bit handling, whereas only an integrated synthetic assertion can prove that the RP applies the same decision to signed authenticator data after the rest of the ceremony checks.
Negative test | Failure it targets | What a green normal test cannot prove |
BE=0, BS=1 | Invalid flag combination accepted | That malformed states are rejected |
Registration BE=1, assertion BE=0 | Immutable value overwritten | That BE permanence is enforced |
Registration BE=0, assertion BE=1 | Same invariant in opposite direction | That comparison is symmetric |
BS 1→0 | State change ignored | That resilience branch executes |
Bad signature plus plausible BE/BS | Trust in unverified flags | That policy effects wait for valid ceremony |
Bad challenge/origin plus plausible flags | Partial verification | That valid flags cannot rescue an invalid assertion |
Failed request followed by valid retry | Hidden partial write | That failure rollback actually worked |
Two concurrent different-BS assertions | Arrival-order assumptions | That “latest” has valid ordering semantics |
WebAuthn requires challenge, origin, RP ID hash, user-presence and relevant user-verification checks in addition to the BE/BS rules, and signature verification covers authenticator data and the client-data hash. A policy implementation that unit-tests only flags & 0x18 has therefore tested one component, not an authentication ceremony.
False confidence also appears when teams interpret one signal as proof of another. The WebAuthn specification itself is careful with signature-counter anomalies: a non-increasing counter can signal possible cloning, authenticator malfunction, or a request-processing race, and is explicitly described as a signal rather than proof. That is a useful discipline for backup-state telemetry too: record exactly what the protocol says before attaching a stronger causal story.
For teams formalizing these cases as regression tests, QA automation engineering is the adjacent discipline; the WebAuthn-specific requirement here is that deliberately broken behavior remains part of the suite rather than being replaced by happy-path coverage.
Operational rollout and rollback criteria
Do not turn on a new authentication consequence merely because a parser can expose two Boolean values.
Before rollout, inventory the credential-record schema, verify where BE and BS are written, identify whether existing records have trustworthy creation-time BE values, and determine what happens when historical data is absent. The W3C model recommends both backupEligible and backupState in the credential record, with different lifecycle definitions.
Rollout gate | Proceed when | Roll back or hold when |
Parsing | BE/BS decoded from correct flag positions | Parser disagreement or unknown masking behavior |
Data model | Immutable BE and mutable BS represented separately | One field conflates both concepts |
Existing records | Migration provenance is understood | BE would need to be guessed |
Invalid combination | 0/1 deterministically fails | Request reaches policy success |
BE mismatch | Deterministic failure/quarantine behavior defined | Code silently overwrites stored BE |
BS transition | Explicit 0→1 and 1→0 branches exist | Generic “changed” handler loses direction |
Persistence | Failed ceremony produces no forbidden state write | Any partial mutation occurs |
Observability | Versioned decision evidence retained | Only generic login success/failure is visible |
Rollback | Feature/policy branch can be disabled without deleting evidence | Rollback requires destructive data edits |
For existing credentials where creation-time BE was never retained, do not manufacture history by copying the next observed BE into a field labeled “BE at registration” without marking its provenance. That next assertion can provide an observation, but it cannot retroactively prove what the RP actually stored at registration.
A practical migration can therefore distinguish records such as be_provenance=registration, be_provenance=first_level3_observation, and be_provenance=unknown. Those names are an engineering proposal, not a WebAuthn requirement, but they avoid making inferred migration data look like protocol evidence.
Rollout should also separate “observe” from “enforce.” An initial policy version can decode, validate structurally, and record transitions while leaving optional local consequences disabled; a later enforcement change can activate the decision branch after fixture, integration, rollback, and concurrency evidence has passed. That staged approach is consistent with the broader change-control discipline described in secure and scalable API engineering.
Treat the policy response as an API contract too. If downstream services consume a reason code such as BACKUP_STATE_CHANGED, document whether it means 0→1, 1→0, or either. The value of explicit machine-readable contracts is closely related to the concerns covered in Refonte's Swagger/OpenAPI article.
Acceptance matrix
The final acceptance test should combine protocol validity, stored state, transition direction, verification outcome, and persistence behavior.
The matrix below is intentionally stricter than “does login work?” because the proposed policy change concerns stateful security decisions. The WebAuthn foundations are that 0/1 is invalid, BE must remain constant, BS may change, and the RP's stored backup state is updated as part of successful processing rather than used as license to rewrite eligibility.
Stored BE | Stored BS | Current BE | Current BS | Other verification | Expected result | Persistent BS |
0 | 0 | 0 | 0 | Pass | Accept | 0 |
0 | 0 | 0 | 1 | Pass | Reject malformed flags | 0 |
0 | 0 | 1 | 0 | Pass | Reject BE contradiction | 0 |
0 | 0 | 1 | 1 | Pass | Reject BE contradiction | 0 |
1 | 0 | 1 | 0 | Pass | Accept | 0 |
1 | 0 | 1 | 1 | Pass | Accept/local 0→1 policy | 1 |
1 | 1 | 1 | 1 | Pass | Accept | 1 |
1 | 1 | 1 | 0 | Pass | Accept/local 1→0 policy | 0 |
1 | 0 | 0 | 0 | Pass | Reject BE contradiction | 0 |
1 | 1 | 0 | 1 | Pass | Reject malformed flags | 1 |
1 | 0 | 1 | 1 | Signature fails | Reject ceremony | 0 |
1 | 0 | 1 | 1 | Additional security check fails | Reject; no state update | 0 |
Whether a valid 1→0 BS transition itself blocks authentication is an RP policy choice; WebAuthn instead describes the resilience implication and recommends guiding the user to validate other factors or add another credential when needed.
Pass criteria
The policy is ready only when the evidence demonstrates all of these conditions together:
Criterion | Required evidence |
Correct bit decoding | Raw byte and expected BE/BS agree for every fixture |
Structural validation | BE=0/BS=1 rejected |
BE permanence | Both directions of BE mismatch rejected under the enabled policy |
BS mutability | 0→1 and 1→0 handled without rewriting BE |
Full-verification boundary | Invalid signature/challenge/origin cannot trigger committed state |
Failed-attempt cleanliness | Before/after state proves no forbidden mutation |
Concurrency awareness | Test documents behavior under overlapping assertions |
Reproducibility | Specification, runtime, fixture, and policy versions recorded |
Evidence semantics | Logs state observation, not unsupported copy/intent claims |
Rollback | Policy can be disabled without corrupting credential records |
A pass does not require claiming that a particular browser, operating system, or credential provider will generate every transition in this matrix. The fixtures exist to validate the relying party's branch logic against WebAuthn semantics independently of those implementation-specific behaviors.
Hold, refactor, or quarantine conditions
The change should not reach enforcement if any of the following remains true:
Condition | Decision |
Code overwrites stored BE on authentication | Refactor |
BE and BS share one ambiguous state field | Refactor |
BE=0/BS=1 reaches successful policy evaluation | Hold |
Historical BE is unknown but treated as proven registration data | Hold |
Failure can update BS before final acceptance | Refactor |
BE mismatch appears in synthetic testing and code continues silently | Quarantine test path and fix policy |
Production mismatch appears but evidence is insufficient | Quarantine decision; investigate without inventing cause |
Log says “credential copied” solely because BS=1 | Refactor telemetry semantics |
Rollback destroys the original state/evidence | Hold |
Test suite has no intentionally failing case | Hold |
“Quarantine” here means separating an anomalous record or policy decision for investigation according to the RP's own operational design; it does not mean that WebAuthn defines a standardized quarantine mechanism.
Most importantly, an eligibility contradiction should not be normalized as a backup-state change. BE and BS answer different questions, and the entire acceptance model fails if the implementation treats them as interchangeable.
Common implementation mistakes
The recurring errors are mostly lifecycle errors rather than bitwise-programming errors. Reading bit 3 and bit 4 is straightforward; preserving their different semantics across registration, authentication, persistence, logging, migration, and policy changes is harder.
Mistake | Why it is wrong | Correction |
Calling BE “current sync status” | BE is permanent eligibility | Store it as creation-time credential property |
Treating BS as immutable | BS may change | Model explicit transitions |
Accepting BE=0/BS=1 | Combination is prohibited | Reject structurally |
Updating BE after every assertion | Erases permanence invariant | Compare with stored BE; do not relearn it |
Treating BS=1 as proof of a particular copying mechanism | WebAuthn allows multiple backup mechanisms | Record only backed-up state |
Treating BS=0 as proof user disabled backup | Errors can also explain the state | Avoid unsupported intent claims |
Writing BS before final checks | Failed request can leave stale state | Stage and commit after successful checks |
Using successful rerun as cleanup proof | It says nothing about earlier side effects | Compare before/after evidence for failed attempt |
Equating server arrival order with backup chronology | Concurrent assertions can be reordered | Treat each BS as a ceremony observation |
Testing only browser UX | Confounds platform behavior with RP protocol logic | Keep raw synthetic fixture tests |
Hiding policy in generic “risk score” | Makes acceptance hard to reproduce | Emit explicit reason codes and version |
Migrating unknown legacy BE as fact | Invents registration history | Preserve provenance/unknown state |
The specification itself gives RPs a useful model by recommending distinct backupEligible and backupState credential-record properties. It also distinguishes a fixed BE from a changing BS during assertion verification.
Another mistake is assuming that because the WebAuthn algorithm examines the backup-policy comparison before its signature-verification step, an application should commit business effects immediately. The same algorithm later updates state after the remaining verification steps and advises deferring updates further when additional security checks exist. Policy evaluation and durable mutation should therefore remain separate concerns.
For teams implementing this inside a broader API service, keep the WebAuthn-specific verification boundary explicit rather than burying it in generic middleware. Refonte's API developer engineering discussion provides the broader engineering context, but BE/BS correctness still needs its own protocol-level tests.
Final decision and Refonte Learning CTA
The policy change should be accepted only after the RP demonstrates, with synthetic evidence, that backup eligibility is treated as an immutable credential-source property while backup state is treated as a mutable observation.
That conclusion follows directly from WebAuthn Level 3. The generating authenticator determines eligibility at credential creation; BE must not change. BS reports current backup status and can change. 0/1 is invalid. An RP using backup state in policy compares the current BE with the stored eligibility and may then apply local policy to BS; successful processing updates stored backup state, while additional security checks should complete before those updates are committed.
The final operational rule can therefore be kept compact:
Observation | Final disposition |
BE=0, BS=1 | Reject as invalid |
Current BE differs from proven registration BE | Fail invariant/policy verification; investigate |
BS unchanged | Continue normal policy |
BS 0→1 | Record verified state observation; apply only documented local policy |
BS 1→0 | Record verified state observation; invoke resilience branch as appropriate |
Ceremony fails after flags were parsed | Commit no prohibited credential-state change |
Cause of BS transition unknown | Keep it unknown |
Browser/platform behavior differs | Investigate implementation separately from WebAuthn semantics |
This policy deliberately refuses a stronger conclusion than the available evidence supports. A backup-state flag is useful because it can drive resilience decisions; it becomes dangerous when the RP turns it into unsupported claims about credential copying, synchronization technology, device identity, or user intent. The WebAuthn definitions and backup mechanisms do not provide that level of causality.
Decision record
Decision-record field | Decision-record value |
Normative baseline | W3C WebAuthn Level 3 Recommendation, August 25, 2026 |
Policy invariant | BE for an existing credential source does not change |
Mutable state | BS may change between ceremonies |
Structurally invalid state | BE=0, BS=1 |
Registration evidence | Persist creation-time BE and initial BS |
Authentication evidence | Compare BE; classify BS transition; apply explicit RP policy |
Test resource type | Synthetic authenticator-data fixtures or virtual authenticator only |
Deliberate failure | Existing BE=1 credential observed with synthetic BE=0 |
Failure acceptance | Rejected with no forbidden persistent mutation |
Recovery proof | Before/after evidence plus separate green rerun |
Causal inference rule | Do not infer copying mechanism or user intent solely from BE/BS |
Browser/platform conclusion | Outside primary acceptance target |
Lab execution status | Proposed; no execution claimed in this article |
The resulting engineering posture is intentionally conservative: trust the bits only after the surrounding WebAuthn verification succeeds, interpret them only according to their specified semantics, preserve the creation-time eligibility invariant, and make every policy consequence independently testable. That is enough to turn “the passkey backup state changed” from an ambiguous alert into a reproducible relying-party decision.
Explore the Refonte Learning API Developer Program. Review its published scope.
