Testing with the Chrome Virtual Authenticator

A virtual authenticator turns every interesting ceremony outcome into something a test can produce on demand: a refusal, a device with no biometric, an authenticator that never answers. This page covers the settings that matter, the setup and teardown that keeps a suite reliable, and the scenarios worth encoding. It sits under Passkey Testing and Conformance.


Capability Reference

Setting Values What it lets you test
protocol ctap2 or u2f modern ceremonies versus legacy behaviour
transport internal, usb, nfc, ble, hybrid which authenticator class the client believes it has
hasResidentKey true or false discoverable credentials and the storage-full fallback
hasUserVerification true or false whether the device can verify a user at all
isUserVerified true or false whether it does on this ceremony
automatic presence on or off prompt satisfied automatically, or left hanging

The last row is the one that unlocks timeout testing, and it is the least documented.

The knobs that matter Five settings on a virtual authenticator cover essentially every scenario worth testing. Five settings, and the last one is how you test timeouts protocol and transport ctap2 with internal, usb or hybrid — drives which flows appear hasResidentKey present or absent — tests the discoverable and allow-list paths hasUserVerification whether the device can verify a user at all isUserVerified whether it does, on this ceremony automatic presence whether a touch is simulated or the request simply hangs

Root Cause Analysis

1. Authenticators leaking between tests. An authenticator added in one test and not removed remains attached for the next, carrying its credentials with it. The symptom is a test that passes in isolation and fails in a suite, or worse, one that passes in a suite for the wrong reason.

2. Testing only with user verification enabled. The default configuration verifies the user automatically, which means every ceremony reports the verification bit set. A policy that requires verification then passes every test and fails for the first user with a security key that has no PIN.

3. Assuming a resident key. With hasResidentKey true, an empty allow-list works. Turn it off and the same ceremony finds nothing — which is exactly the behaviour of a real authenticator whose storage is full, and exactly the path most suites never exercise.

4. Waiting out real timeouts. A test that configures an authenticator not to answer and then waits for the browser’s default window will take minutes. Set a short timeout in the ceremony options instead; the code path is identical.

The lifecycle of a virtual authenticator Enable the virtual environment, add an authenticator, run ceremonies, then remove it so the next test starts clean. Four steps, and the fourth is the one people forget enable the virtual environment add an authenticator with a config run registrations and assertions remove before the next test Leaving an authenticator attached between tests is the most common source of tests that pass alone and fail in a suite.

Step-by-Step Resolution

Step 1 — enable the virtual environment before adding anything

The environment is a session-level toggle; adding an authenticator without it silently does nothing in some drivers.

Step 2 — add an authenticator per scenario, not per suite

Each test should describe the device it needs. Sharing one configuration across a suite means changing it for one test breaks the others.

Step 3 — remove it in teardown

Unconditionally, including after a failed assertion. A test framework that skips teardown on failure will cascade one failure into several.

Step 4 — configure the scenario explicitly

Never rely on defaults. Stating hasUserVerification and isUserVerified in every test makes the intent readable and stops a driver default change from silently altering what is being tested.

Step 5 — assert on your own telemetry as well as the outcome

The interesting assertion in a failure test is often not the error but the reason code your server recorded, because that is what your dashboards will show.

Two ways to simulate a failure Refusing and never answering produce different errors and exercise different code paths. Both are needed; they are not interchangeable configured to refuse the ceremony rejects promptly NotAllowedError is returned tests the cancellation path fast in a suite configured not to answer the ceremony stays pending rejects when the window elapses tests the timeout path needs a short configured window Set a short timeout in the ceremony options so the second case does not stall the suite for minutes.

Verification and Testing

Prove the harness itself works before trusting it. Register a credential against a virtual authenticator, then authenticate with it, and assert the assertion verifies server-side. If that round trip does not work, nothing built on top of it means anything.

Then prove isolation: run the same registration test twice in one suite and assert both succeed. If the second fails with a duplicate refusal, teardown is not running.

Finally, prove the negative configurations do what you think. A test asserting that a ceremony fails should also assert how it failed — a test that passes because the driver threw a configuration error rather than because the ceremony was refused is a test that will keep passing after the behaviour it guards has broken.


Pitfalls

1. One shared authenticator for the whole suite. Couples every test to every other test.

2. Relying on automatic user verification. Hides every unverified-ceremony bug.

3. Asserting only that an error occurred. Assert the name and, where you have one, the server-side reason code.


Frequently Asked Questions

Does a virtual authenticator produce real cryptography?

Yes. It generates genuine key pairs and produces genuine signatures, which is why an assertion from one verifies against your real server-side code. What it does not simulate is hardware key storage, secure-element behaviour, or anything requiring a physical interaction.

Can I test attestation with one?

To a limited extent. A virtual authenticator produces attestation statements, but they will not chain to a vendor root you would trust in production, so a policy that requires a verified chain will reject them. Attestation verification is better covered by captured fixtures than by the virtual environment.

Does it work in headless mode?

Yes, which is the point — the whole arrangement exists so that ceremony behaviour can be tested in continuous integration without a person present. The virtual environment is driven through the same automation protocol as the rest of the browser.

How do I test the cross-device flow?

You cannot. Setting the transport to hybrid changes what the client believes about the authenticator, which is useful for testing how your interface presents the option, but the actual flow depends on a Bluetooth proximity exchange and a relay that no virtual device provides.

Can I simulate multiple authenticators at once?

Yes, and it is worth doing for the allow-list tests: two authenticators, one holding a credential and one not, proves that the client routes the ceremony using the transports you supplied rather than by asking everything in turn.

Should these tests run on every commit?

Yes. They are fast — the ceremonies complete in milliseconds — and they cover the layer where user-visible regressions actually happen. Reserve the slower conformance tooling for pre-release.



A Scenario Matrix Worth Encoding

Once the harness is reliable, the useful exercise is enumerating device configurations rather than test cases, because each configuration exercises a different branch of your production code with no extra assertions.

Internal transport, resident key, verification available and performed. The modern platform passkey. This is the configuration most of your users have, and it should be the default in tests that are not specifically about something else.

Internal transport, resident key, verification available but not performed. A device where the user declined or the platform did not require it. Your policy for verified actions must reject this ceremony, and the test proves the flag is actually read rather than assumed.

USB transport, no resident key, no verification. An older hardware security key. This configuration breaks two things at once in most implementations: an empty allow-list finds nothing, and a policy requiring verification cannot be satisfied. Both are legitimate real-world states.

USB transport, resident key, verification available. A modern security key with a PIN. Useful as the counterpart to the previous case, and the configuration to use when testing enterprise-style policies.

Two authenticators attached, one holding a credential. Proves the allow-list and its transports actually route the ceremony instead of the client interrogating every device in turn.

Any configuration with automatic presence disabled. The abandoned ceremony, and the only way to exercise the timeout path without waiting.

Six configurations, each a few lines of setup, and between them they cover every branch that a real population will hit. Naming them as named fixtures in your test helpers — rather than inlining the configuration in each test — keeps the intent readable and means adding a seventh later is a single change.

Related