Resource library

QA How-To

How to Test Passkey Authentication Flows (2026)

Learn how to test passkey authentication flows across enrollment, sign-in, recovery, devices, browsers, security boundaries, and failure paths safely.

17 min read | 3,245 words

TL;DR

Test passkeys as an end-to-end security protocol, not just a biometric prompt. Exercise registration, sign-in, cancellation, duplicate enrollment, recovery, device changes, and server validation on real and virtual authenticators.

Key Takeaways

  • Test registration and authentication as ceremonies with separate browser, authenticator, and server checkpoints.
  • Cover discoverable and username-first sign-in because they produce different request options and account-selection behavior.
  • Verify server-side challenge, origin, RP ID, user-presence, and user-verification checks instead of trusting a successful dialog.
  • Use real devices for platform, roaming, synced, and cross-device passkeys, then add virtual authenticators for repeatable regression tests.
  • Treat recovery, passkey deletion, session handling, and audit events as part of the authentication feature.
  • Never log raw WebAuthn responses, session tokens, or unnecessary credential identifiers in test evidence.

To learn how to test passkey authentication flows, split the feature into registration, authentication, credential management, recovery, and session security. A passkey prompt that opens and closes successfully proves very little by itself. You must confirm that the browser receives correct WebAuthn options, the authenticator performs the expected gesture, and the server validates every cryptographic and contextual property.

This tutorial gives you a runnable local test page, a manual passkey testing checklist, a repeatable virtual-authenticator test, and concrete negative cases. It assumes your application uses the standard Web Authentication API rather than a vendor-specific biometric API.

TL;DR

Approach Best evidence Main limitation Use it for
Real platform authenticator Actual OS prompt, biometric or device PIN, sync behavior Hard to reset and less deterministic Release acceptance on supported phones and laptops
Real roaming security key USB, NFC, or BLE interaction and portability Requires physical inventory Hardware-key compatibility and user-presence checks
Cross-device authentication QR and proximity handoff between devices Depends on radios, camera, and ecosystem state Desktop sign-in using a phone-held passkey
Browser virtual authenticator Repeatable credentials and controllable user verification Does not validate native UI or hardware CI regression, malformed-state tests, and protocol assertions

Use all four where they match your support statement. Real-device testing answers whether customers can complete the journey. Virtual-authenticator testing answers whether the web application handles protocol states consistently.

What You Will Build

By the end, you will have:

  • A local HTTPS-capable WebAuthn test surface backed by an in-memory server.
  • A registration and sign-in charter covering happy paths and adverse states.
  • A browser matrix for platform, roaming, synced, and cross-device credentials.
  • A Playwright test that creates a Chrome DevTools Protocol virtual authenticator.
  • Server-side assertions for challenges, RP identity, counters, sessions, and audit events.

The result is a compact test pack that supports exploratory sessions and regression automation without pretending that automation replaces physical-device coverage.

Prerequisites

Use a current Node.js LTS release, npm, and a Chromium-based browser. The sample uses @simplewebauthn/server and @simplewebauthn/browser, which prevent you from hand-coding CBOR and signature validation. Install the current compatible releases in a disposable project:

mkdir passkey-test-lab
cd passkey-test-lab
npm init -y
npm install express @simplewebauthn/server @simplewebauthn/browser
npm install --save-dev @playwright/test typescript tsx
npx playwright install chromium

Passkeys require a secure context. Browsers treat http://localhost as trustworthy for local development, but a remote test host must use HTTPS. Give each tester a clean account, access to at least one supported phone and desktop, and a known fallback method. Record the browser version, OS build, authenticator type, transport, account state, and whether credential sync is enabled.

Before testing, review authentication flaw testing, session management testing, and rate limiting and brute-force testing. Those controls still matter after passwords disappear.

Step 1: Map the Passkey Ceremonies and Trust Boundaries

Draw two sequences. Registration calls navigator.credentials.create() with options derived from the server. Authentication calls navigator.credentials.get() with a new server challenge. In both sequences, JavaScript only transports data. The relying-party server decides whether the result is valid.

List these boundaries in the test plan:

  1. The account and session that request enrollment.
  2. The endpoint that creates a one-time challenge.
  3. The browser origin, such as https://login.example.com.
  4. The RP ID, normally example.com or the exact host.
  5. The authenticator and its user-presence or user-verification result.
  6. The verification endpoint that consumes the challenge and stores or reads the public key.
  7. The session issued after successful authentication.

For registration, inspect the options response. Confirm rp.id, user.id, user.name, challenge, supported algorithms, timeout, authenticator selection, attestation preference, and excludeCredentials. For authentication, inspect challenge, rpId, allowCredentials, user-verification preference, and extensions. Do not assert exact random challenges. Assert that they are nonempty, sufficiently unpredictable by implementation design, bound to the current transaction, single-use, and expired after the configured window.

Verify this step by tracing one registration and one sign-in in browser developer tools. Each should have one options request and one verification request. A captured challenge from registration must not authenticate a user, and a sign-in challenge must not enroll a credential.

Step 2: Establish Accounts, Authenticators, and Browser Coverage

Build the matrix from supported customer journeys, not from whatever devices happen to be nearby. Include a platform authenticator on each supported OS family, a roaming FIDO2 key if promised, a synced passkey account, and a cross-device journey. Add private browsing only if the product claims it works there.

Create at least four account states: no passkey, one passkey with fallback, multiple named passkeys, and passkey-only. If administrators can require phishing-resistant authentication, add an account under that policy. Keep a separate locked, disabled, and deleted account so authentication cannot accidentally restore access.

For every matrix cell, record whether the browser offers discoverable sign-in, displays the correct account chooser, allows another device, and returns intelligible cancellation or timeout feedback. Platform wording varies, so assert application behavior rather than exact native-dialog text. A web test usually cannot inspect the protected biometric dialog, and it should not try to infer which finger or face was used. Your product receives an authenticator assertion, not biometric data.

Verify the matrix with a short smoke run. Each supported combination must complete registration and sign-in once. Mark unsupported combinations explicitly with the expected product message. An unexplained blank screen, generic server error, or endless spinner is a defect even when the authenticator itself is unsupported.

Step 3: Test Passkey Registration From Start to Finish

Begin with an authenticated account and a fresh authenticator. Select Add passkey, inspect the options response, complete user verification, and confirm that the application names the new credential without exposing its raw ID. Sign out, then use that passkey to return. This last action proves the stored public key is usable, not merely present in a settings row.

Run registration variations deliberately:

  • Cancel before touching the sensor or entering the device PIN. The page should return to a usable state and allow retry.
  • Let the prompt expire. The server must reject a late response, and the UI should distinguish timeout from account failure.
  • Enroll a second credential on another device. Both should remain independently usable.
  • Attempt to register an already enrolled authenticator. excludeCredentials should prevent a duplicate or the server should reject it safely.
  • Change the account email or display name. The stable WebAuthn user handle should still map to the same internal user.
  • Start enrollment in one tab and finish another enrollment first. Each response must remain bound to its own challenge and session.
  • Lose the login session while the native prompt is open. Verification must not attach the credential to an unauthenticated or different account.

If attestation is set to none, do not expect device model proof. If enterprise policy needs attestation, test accepted and rejected trust chains with security specialists. Avoid turning attestation metadata into a brittle browser-brand check.

Verify registration in three places: the user sees a success state, account settings show one new credential with a safe label and timestamp, and the audit trail identifies enrollment without storing the attestation object or client data wholesale.

Step 4: Test Passkey Authentication Flows and Account Selection

This is the central answer to how to test passkey authentication flows: exercise both username-first and discoverable sign-in. They can share server verification but have different option generation and account-discovery behavior.

In username-first sign-in, enter an identifier and inspect allowCredentials. It should include only credentials authorized for that account, unless the design intentionally uses discoverable credentials without a list. Complete the assertion, verify the signature, and check that the resulting session belongs to the requested account. Enter another user's identifier and ensure your authenticator cannot cross the account boundary.

In discoverable sign-in, trigger the passkey action without entering a username. The authenticator returns a user handle that the server maps to an account. Test one credential, multiple account credentials, and a credential whose server-side account is disabled. Never choose the account using untrusted display text from the client.

Cover conditional mediation if the login form uses browser autofill. Focus the username field, select the passkey suggestion, cancel it, type a password fallback, and return to the suggestion. Confirm that feature detection failure leaves a working login form. Also test two rapid clicks, browser Back during the prompt, page refresh, offline transition, and a verification response replay.

A successful response should rotate or upgrade the application session according to policy. Follow the bearer token refresh testing guide when APIs issue access tokens. Verify that the old pre-authentication session cannot be fixed or reused and that Logout invalidates the authenticated state.

Step 5: Challenge Server Validation With Negative Tests

A polished user journey can hide a weak verifier. Intercept requests in a controlled test environment and modify one property at a time. The expected result is a generic authentication failure, no session, no credential mutation, and a security event with safe diagnostic detail.

Test these server decisions:

  • Replay an already accepted assertion. The consumed challenge must fail even if the signature is otherwise valid.
  • Submit an assertion after challenge expiry or against a different browser session.
  • Change the expected origin from https://login.example.com to an attacker-controlled or lookalike origin.
  • Validate against the wrong RP ID. RP ID mismatch must fail cryptographic verification.
  • Send a credential ID that is unknown, belongs to another user in username-first mode, or has been revoked.
  • Require user verification, then provide an assertion whose UV flag is false. User presence alone is not equivalent to user verification.
  • Corrupt clientDataJSON, authenticatorData, or the signature. The endpoint should return a controlled 4xx response rather than crash.
  • Remove required fields, duplicate JSON keys at the parser boundary, send oversized payloads, and use invalid Base64URL encoding.

Signature counters need nuanced tests. Some authenticators return a counter that increases; others legitimately report zero. Follow the library and product risk policy rather than rejecting every zero counter. If a previously increasing counter moves backward or repeats unexpectedly, create a risk signal without locking out legitimate synchronized credentials blindly.

Use JSON response schema validation for endpoint contracts and sensitive data exposure testing for responses and logs. Verify every negative case by checking the HTTP response, absence of an authenticated cookie, unchanged credential record, and sanitized audit entry.

Step 6: Exercise Recovery, Revocation, and Lifecycle Changes

A user who loses every device still needs a safe path, while an attacker must not downgrade passkey protection cheaply. Test recovery as carefully as sign-in. Start with an account that has one passkey, remove access to that authenticator, and follow each documented recovery method. Confirm identity proofing, notification, cooling-off rules, and session invalidation according to product policy.

Delete one of several passkeys in account settings. The deleted credential must fail immediately, while the remaining credential continues to work. Rename credentials with blank, long, Unicode, duplicate, and markup-like labels. The UI should normalize or reject unsafe input and render labels as text. Delete the final passkey and confirm whether fallback is required before the action.

Change password, email, phone number, and MFA policy. Document which actions revoke passkeys, which require recent authentication, and which only notify the user. There is no universal correct coupling, but behavior must match the threat model and user-facing promise. Disable and re-enable the account; no passkey should bypass the disabled state. Delete the account and verify orphaned credential IDs cannot authenticate or reveal whether the old account existed.

Review sessions after passkey removal. A product may preserve existing sessions or revoke them, but the choice must be deliberate. Test other active devices and privileged actions. Pair these checks with CSRF testing because credential-management endpoints change security state even though WebAuthn resists credential phishing.

Verify the lifecycle step from the public API or account UI plus the audit log. Events should distinguish add, rename, remove, recovery start, recovery completion, and failed assertion without storing secrets.

Step 7: Validate Cross-Device, Sync, Accessibility, and Privacy

Cross-device authentication commonly starts on a desktop and uses a phone-held passkey after a QR scan and proximity check. Test correct phone, wrong phone, camera denial, Bluetooth disabled, airplane mode, QR expiry, repeated scan, canceled handoff, desktop refresh, and completion after the desktop session expires. A screenshot of an old QR code must not remain useful indefinitely.

For synced passkeys, register on one ecosystem device, wait for normal synchronization, and sign in on another signed-in device. Then test sync disabled, cloud account signed out, device lock removed, and credential deleted from one location. Do not promise instant propagation unless the platform guarantees it. Report whether the failure belongs to the product, browser, OS, or ecosystem account.

Test keyboard access to every web control before and after the protected native prompt. Focus must return to a meaningful element after cancellation, errors must be announced, and color must not be the only status signal. Zoom the page, increase text size, and use a screen reader on the web content. The OS biometric surface is owned by the platform, but your launch button, guidance, fallback, and result message are your responsibility. The keyboard navigation testing guide and focus management testing guide provide deeper checks.

Inspect analytics, network traces, console output, support diagnostics, and screenshots. They must not expose challenge payloads unnecessarily, raw credential objects, session cookies, biometric claims, or misleading device fingerprints. Verify that telemetry can still answer which ceremony failed, at which product stage, and on which broad platform without collecting authentication material.

Step 8: Automate Stable Protocol States With a Virtual Authenticator

Use a Chromium virtual authenticator for repeatable regression. The following Playwright test opens a CDP session, enables WebAuthn, adds a resident-key authenticator with user verification, and drives an application through registration and sign-in. Replace selectors and the base URL with your test surface.

import { test, expect } from '@playwright/test';

test('registers and uses a passkey', async ({ page, context }) => {
  const client = await context.newCDPSession(page);
  await client.send('WebAuthn.enable');
  const { authenticatorId } = await client.send(
    'WebAuthn.addVirtualAuthenticator',
    {
      options: {
        protocol: 'ctap2',
        transport: 'internal',
        hasResidentKey: true,
        hasUserVerification: true,
        isUserVerified: true,
        automaticPresenceSimulation: true,
      },
    },
  );

  await page.goto('https://passkeys.test.local/settings/security');
  await page.getByRole('button', { name: 'Add passkey' }).click();
  await expect(page.getByText('Passkey added')).toBeVisible();

  const credentials = await client.send('WebAuthn.getCredentials', {
    authenticatorId,
  });
  expect(credentials.credentials).toHaveLength(1);

  await page.getByRole('button', { name: 'Sign out' }).click();
  await page.getByRole('button', { name: 'Sign in with a passkey' }).click();
  await expect(page).toHaveURL(/dashboard/);

  await client.send('WebAuthn.removeVirtualAuthenticator', { authenticatorId });
});

Run it with npx playwright test. Expected output is one passing test, one credential after registration, and a dashboard URL after authentication. Put cleanup in a fixture or try/finally in production code so a failed assertion does not leave emulator state behind.

Add separate tests that call WebAuthn.setUserVerified with isUserVerified: false, disable automatic presence simulation to model timeout, remove the virtual authenticator, and delete credentials. Keep real-device acceptance outside this suite. If you are new to the runner, use the Playwright TypeScript framework tutorial.

Which Should You Choose

Choose real platform authenticators for release gates involving native prompts, biometric or PIN fallback, credential sync, and supported OS behavior. Choose roaming keys when enterprise users carry hardware authenticators. Choose cross-device sessions whenever the product advertises phone-assisted desktop sign-in. Choose virtual authenticators for pull-request regression, deterministic negative states, credential inspection, and fast cleanup.

A balanced strategy usually has three layers. Run virtual-authenticator tests on every relevant change. Run a small real-device smoke matrix before release. Schedule broader ecosystem, accessibility, lifecycle, and recovery sessions when authentication code, browser support, or policy changes. Risk determines frequency: a passkey-only financial account deserves more physical-device and recovery coverage than an optional convenience login for a low-risk service.

Do not select a tool based only on automation percentage. Select the evidence needed. CDP can prove that your page responds to a WebAuthn ceremony, but it cannot prove a phone camera scans your QR code, a security key works over NFC, or platform guidance is understandable. Manual testing can expose those problems, while server-focused automation catches replay and validation regressions that a person may miss.

Troubleshooting

SecurityError before a prompt appears -> Confirm HTTPS or localhost, exact RP ID scoping, and a permitted iframe policy. A subdomain cannot claim an unrelated RP ID.

NotAllowedError after waiting -> Separate user cancellation, timeout, missing user gesture, and policy denial using surrounding state. Browsers intentionally keep WebAuthn errors somewhat opaque.

Registration succeeds but sign-in says unknown credential -> Compare the persisted credential ID and public key encoding with the library's expected binary or Base64URL format. Also verify the user handle mapping.

Virtual authenticator never completes -> Enable WebAuthn before the ceremony, set automatic presence simulation, and ensure the authenticator supports the resident-key and verification properties requested by the application.

Works on localhost but fails in staging -> Check HTTPS certificates, RP ID, origin allowlist, reverse-proxy host handling, cookie attributes, and whether staging is embedded in an iframe.

Cross-device QR appears but the phone cannot finish -> Check QR expiry, Bluetooth and proximity requirements, camera access, network reachability, and ecosystem support before filing it as a server defect.

Interview Questions and Answers

A strong interview explanation separates ceremony success from server trust. Discuss the validation point, authenticator coverage, recovery risk, and why virtual devices supplement physical devices. The model answers in the structured interview section below cover challenge replay, RP ID versus origin, user presence versus verification, counters, cancellation, and automation scope.

When describing experience, name the evidence you collected: options responses, verification results, session state, credential lifecycle, sanitized audit events, and physical-device observations. This is more credible than saying you tested biometric login successfully. Practice explaining your approach in the QA interview practice area.

Common Mistakes

  • Testing only enrollment and assuming the stored credential can authenticate later. Always sign out and use it.
  • Treating the native prompt as proof that server verification is correct. Replay and alter requests in an authorized test environment.
  • Calling every passkey a biometric. Authenticators may use a device PIN, pattern, or security-key gesture, and biometric data stays local.
  • Requiring an increasing signature counter from authenticators that legitimately return zero. Apply the implementation's risk policy.
  • Ignoring discoverable sign-in because username-first login passes. Account selection and user-handle mapping add distinct risks.
  • Running only a virtual authenticator. It cannot cover sync, proximity, native accessibility, USB, NFC, BLE, or real recovery friction.
  • Logging complete WebAuthn payloads for debugging. Capture the minimum safe diagnostics and control access to evidence.
  • Forgetting credential-management authorization. Add, remove, rename, and recovery actions need recent authentication and CSRF protection where appropriate.
  • Using shared test accounts across parallel device sessions. Credential and challenge state becomes ambiguous and can hide account-mapping defects.
  • Showing an endless spinner after cancellation. Restore focus, explain the outcome without leaking account state, and provide a retry or fallback.

Where To Go Next

Convert the matrix into a release checklist with named device owners and evidence requirements. Automate registration, authentication, cancellation, replay rejection, and credential revocation where your environment allows it. Keep QR handoff, sync, security-key transports, native prompts, and recovery usability in scheduled manual sessions.

Strengthen adjacent controls with authentication flaw testing, session management testing, and broken access control testing. If the exercise reveals gaps in your resume or portfolio, compare the work against a target role in the QAJobFit resume workspace.

Conclusion

The reliable way to test passkey authentication flows is to validate the whole trust chain: server options, browser ceremony, authenticator behavior, verification, account mapping, credential lifecycle, and the resulting session. Cover username-first and discoverable entry points, then attack challenge reuse, origin and RP ID handling, cancellation, recovery, and cross-device state.

Use virtual authenticators for fast, repeatable protocol regression and real devices for customer reality. Together they reveal both invisible server failures and the practical friction that determines whether users can safely replace passwords.

Interview Questions and Answers

How would you test a passkey authentication feature?

I would separate registration, authentication, credential management, recovery, and session handling. I would run username-first and discoverable journeys on supported real authenticators, then automate stable protocol states with virtual authenticators. Negative tests would target challenge reuse, expiration, origin, RP ID, user verification, account mapping, revoked credentials, and malformed payloads.

What is the difference between RP ID and origin in WebAuthn testing?

The RP ID defines the relying-party scope to which a credential is bound, usually a registrable domain or host. Origin includes scheme, host, and port and identifies the page that initiated the ceremony. The server must validate the expected origin and RP ID relationship because checking only one can allow an invalid context.

How do user presence and user verification differ?

User presence shows that a person interacted with the authenticator, such as touching a security key. User verification shows that the authenticator verified the local user through an approved method such as a PIN or biometric. If policy requires verification, an assertion with only the UP flag must not be accepted.

Why is challenge replay a critical passkey test?

The signature alone does not make an old assertion fresh. A unique, transaction-bound, expiring, single-use challenge prevents an intercepted response from being submitted again. I verify rejection, absence of a session, unchanged credential state, and a safe audit event.

How should a server handle WebAuthn signature counters?

It should follow authenticator capabilities and the server library's risk guidance. Some authenticators increment counters, while valid synchronized credentials may report zero. A suspicious rollback can raise a clone-risk signal, but an unconditional greater-than rule can lock out legitimate users.

What can a virtual authenticator test and what does it miss?

It can deterministically create credentials, control user-verification and presence states, inspect stored credentials, and support fast registration and authentication regression. It does not prove native prompt usability, physical biometric or PIN behavior, cloud sync, proximity, QR scanning, or USB, NFC, and BLE compatibility.

Frequently Asked Questions

Can passkey authentication be tested without a physical biometric device?

Yes. Chromium virtual authenticators can simulate resident credentials, user presence, and user verification for repeatable web tests. You still need physical devices to validate native prompts, biometric or PIN fallback, sync, hardware transports, and cross-device handoff.

What are the most important passkey login test cases?

Prioritize successful username-first and discoverable sign-in, cancellation, timeout, multiple accounts, disabled accounts, unknown credentials, replayed challenges, wrong origin or RP ID, revoked credentials, and session creation. Add cross-device, synced credential, and recovery cases when the product supports them.

Should QA verify the user's fingerprint or face during a passkey test?

No. The relying party does not receive biometric data or learn which local unlock method was used. QA should verify that the authenticator reports the required user-verification state and that the application handles success, refusal, and fallback correctly.

How do I test passkey challenge replay protection?

Capture an accepted authentication response in an authorized test environment and submit it again. The server should reject it because the challenge is single-use, issue no new session, leave credential state safe, and record a sanitized failure event.

Do passkeys eliminate the need to test sessions and account recovery?

No. Passkeys improve resistance to phishing and credential theft, but the application still creates sessions and needs a recovery policy. Weak recovery, session fixation, insecure credential removal, or broken authorization can defeat a strong WebAuthn ceremony.

Which browsers should be included in passkey testing?

Test every browser and OS combination in the published support statement, plus the ecosystem paths customers actually use. Include platform credentials, roaming keys, conditional UI, and cross-device authentication only where each capability is claimed.

Related Guides