This guide shows how to write browser-based end-to-end tests that cover the full passkey lifecycle: enrolling a passkey, then using it to complete a challenge. The test drives your app exactly like a user would, letting it redirect into your identity provider and on to Authsignal. This exercises your policy and user-flow configuration alongside the passkey ceremony.Documentation Index
Fetch the complete documentation index at: https://docs.authsignal.com/llms.txt
Use this file to discover all available pages before exploring further.
The challenge with passkey E2E tests
Passkeys are backed by hardware-bound or password-manager-synced credentials. A regular Playwright or Selenium test can’t unlock Touch ID, summon a security key, or open Apple Passwords. Chromium ships with the WebAuthn DevTools Protocol domain, which lets you install a virtual authenticator. Once installed, everynavigator.credentials.create and navigator.credentials.get call resolves against that virtual authenticator instead of real platform hardware. Playwright exposes this via CDP sessions.
Prerequisites
- An Authsignal tenant. We recommend a dedicated tenant for testing so test traffic doesn’t pollute production analytics.
- The passkey authenticator enabled for the tenant.
- An app integrated with either the pre-built UI, the Web SDK, or an identity provider integration.
- Playwright installed in your project.
Setting up the virtual authenticator
The helper below enables the WebAuthn CDP domain and adds a platform-style virtual authenticator that supports resident keys and user verification. This is the closest analogue to a real platform authenticator like Touch ID.utils/page-helpers.ts
transport: "internal"makes the credential behave like a platform authenticator. Use"usb"or"nfc"if you want to simulate a roaming security key.hasResidentKey: trueenables discoverable credentials so passkey autofill works.isUserVerified: trueandautomaticPresenceSimulation: truelet the test bypass biometric/touch prompts.
Provision a test user
Each test needs a known user in your identity provider. Stand this up as a fixture so it runs once per test suite:- Azure AD B2C: create users via the Microsoft Graph API.
- Auth0: use the Management API
POST /api/v2/users. - Cognito: use
AdminCreateUserwith a known password. - Keycloak: use the Admin REST API.
- Other or custom identity providers will have their own admin APIs to programmatically create users.
Drive the journey end-to-end
Navigate to your app’s normal login URL and complete the user login via primary factor first. If your application is passwordless, this will just be providing the username. A realistic test covers two passes: an enrollment pass that creates the passkey, and a re-authentication pass that signs the same user back in using the credential just enrolled. The virtual authenticator persists for the life of theBrowserContext, so both passes share the same credential.
Depending on your integration, this may either redirect to Authsignal’s pre-built UI or your custom UI which supports passkey enrollment and authentication.
tests/passkey.spec.ts
WebAuthn.credentialAdded and WebAuthn.credentialAsserted events fire when the virtual authenticator actually completes a ceremony, so they’re a reliable signal that the WebAuthn call resolved successfully before you make further DOM assertions.
Other test frameworks
This guide is written for Playwright, but the same virtual-authenticator approach works in any framework that speaks either the Chrome DevTools Protocol or the W3C WebAuthn Automation extension:- Selenium 4: first-class support via the W3C WebDriver extension. See Virtual Authenticator in the Selenium docs. Works across Chrome, Edge, and Firefox.
- WebdriverIO: exposes the same WebDriver extension. See
addVirtualAuthenticator. - Puppeteer: uses raw CDP, the same protocol Playwright uses under the hood. Open a
CDPSessionand call theWebAuthndomain directly.
Next steps
- Review passkey best practices on web before deciding which actions to cover with E2E tests.

