Skip to main content
This guide shows how to test passkey enrollment and sign-in through your application with Playwright. The browser creates and signs a WebAuthn credential, so the test covers your UI, Authsignal configuration, and server-side verification without requiring Touch ID or a physical security key.

Choose a Playwright API

Start with browserContext.credentials unless a test needs one of the Chrome-specific controls.

Prerequisites

  • An Authsignal tenant. Use a dedicated test tenant so automated traffic does not affect production users or analytics.
  • Passkeys enabled for the tenant.
  • An application integrated with Authsignal’s pre-built UI, Web SDK, or an identity-provider integration, with a way to provision and reset test users.
  • Playwright 1.61 or later.

Test enrollment and sign-in together

Install the virtual authenticator before the page first calls navigator.credentials.create() or navigator.credentials.get(). It is scoped to the BrowserContext, so enrollment and sign-in can share the same passkey. The example below uses a custom UI built with the Authsignal Web SDK. Replace the routes and selectors with those from your application. With the Authsignal pre-built UI or an identity-provider integration, the navigation will differ but the assertions remain the same.
tests/passkey.spec.ts
context.credentials.get() confirms that the browser created a credential for the expected relying party. The final UI assertion confirms that Authsignal accepted it and the application established a session.

Reuse credentials across tests

You can capture a credential after enrollment and seed it into another browser context:
Playwright can also include virtual WebAuthn credentials in its storage state:
Restoring that storage state installs the virtual authenticator automatically. The stored credential contains its private key, so treat the file as a secret and keep it out of version control.

Use CDP for advanced Chromium tests

Playwright’s first-class API covers most enrollment and sign-in tests. Chrome’s CDP integration adds failure injection, authenticator events, and credential-state controls.
utils/page-helpers.ts
The main options are:
  • transport: "internal" models a platform authenticator such as Touch ID or Windows Hello. Use "usb" or "nfc" to model the reported transport of a roaming security key.
  • hasResidentKey: true allows discoverable credentials, which are required for username-free sign-in and passkey autofill.
  • hasUserVerification: true declares support for user verification. isUserVerified: true makes that verification succeed.
  • automaticPresenceSimulation: true resolves user-presence checks without waiting for a physical touch.
Chrome recommends enableUI: false for automated tests. Set it to true in a headed debugging session if you want Chrome’s WebAuthn UI to remain visible.
A CDP session only works with Chromium. Do not install both CDP and browserContext.credentials in the same browser context.

Wait for authenticator events

Register the event listener before the action that starts WebAuthn:
The event confirms that the authenticator created a credential, not that the server accepted it. Keep the application-level assertion after the event.

Test failure paths with CDP

User verification fails

Set userVerificationRequirement to "required" in the tenant’s passkey authenticator configuration, then make user verification fail on the next ceremony:
This makes verification fail; it does not simulate a user dismissing an operating-system prompt.

Invalid authenticator response

Override parts of the response to confirm that invalid assertions are rejected:
isBogusSignature replaces the signature with zeros. isBadUP clears the user-presence flag, and isBadUV clears the user-verification flag. A bogus signature and missing user presence must be rejected. Whether a missing user-verification flag is invalid depends on the requirement in the WebAuthn request. After each test, reset the override. Any field omitted from setResponseOverrideBits is reset to false.

User presence times out

Turn off automatic presence simulation to leave the ceremony pending:
Use this to test your application’s abort or timeout behavior. Explicitly cancel the request, navigate away, or wait for the timeout the application promises to enforce instead of adding a fixed sleep.

Stale credential

To test a stale passkey, enroll it in the browser, delete the corresponding authenticator through the Authsignal Server API, and attempt sign-in again. With credential syncing enabled, the Web SDK handles the unknown_credential response and the CDP authenticator emits credentialDeleted:
This confirms that the stale credential was removed from the browser.

Device-bound and backed-up credentials

The WebAuthn backup eligibility (BE) and backup state (BS) flags describe whether a credential can be backed up and whether it is currently backed up. Change them for a test with CDP:
The combinations represent:
  • false / false: a single-device credential
  • true / false: a multi-device credential that is not currently backed up
  • true / true: a backed-up multi-device credential
Authsignal exposes these values as credentialDeviceType and credentialBackedUp on the enrolled authenticator.

Test passkey autofill separately

Passkey autofill uses conditional mediation and requires a discoverable credential. Configure it as described in Using passkey autofill, then test it separately from button-triggered sign-in. A conditional request can remain pending while the page is open, so starting another WebAuthn request at the same time can introduce a race.

Other test runners

Virtual authenticators are not specific to Playwright:
  • Selenium 4: uses the WebAuthn virtual-authenticator commands from the WebDriver specification. See Selenium’s Virtual Authenticator documentation.
  • WebdriverIO: exposes the same WebDriver commands, including addVirtualAuthenticator.
  • Puppeteer: can open a CDP session and use the same WebAuthn.* methods shown above.

What a virtual authenticator will not catch

A virtual authenticator is suited to protocol and integration testing. It does not faithfully reproduce:
  • Apple Passwords, Google Password Manager, or Windows Hello account pickers
  • A real biometric or security-key prompt
  • Phone-assisted QR authentication
  • Passkey sync between physical devices
  • The range of AAGUID and attestation behavior seen on production authenticators
Keep real-device coverage for these cases. Review passkey best practices on web when deciding which UX behavior to test outside CI.