Get your free and exclusive 80-page Banking Passkey Report
passkeys e2e playwright testing webauthn virtual authenticator

E2E-Tests für Passkeys mit Playwright über den virtuellen WebAuthn-Authenticator

Erfahren Sie, wie Sie E2E-Tests für Passkeys browserübergreifend mit Playwright, Nightwatch, Selenium und Puppeteer unter Verwendung des virtuellen WebAuthn-Authenticators einrichten.

Blog-Post-Author

Anders

Created: June 17, 2025

Updated: June 20, 2025


Unsere Mission ist es, das Internet zu einem sichereren Ort zu machen, und der neue Login-Standard Passkeys bietet eine überlegene Lösung, um dies zu erreichen. Deshalb möchten wir Ihnen helfen, Passkeys und ihre Eigenschaften besser zu verstehen.

1. Einführung: E2E-Tests für Passkeys#

Passkeys werden als Authentifizierungsmethode immer breiter akzeptiert und stützen sich auf die Web Authentication (WebAuthn) als zugrunde liegenden Standard. Ihr Popularitätsanstieg ist recht neu, was Dokumentationen und andere Ressourcen relativ rar macht. Dies, zusammen mit der komplexen Natur der Implementierung von Passkeys, kann es für Entwickler schwierig machen, relevante Informationen zum Entwerfen, Implementieren und insbesondere zum Testen von Passkeys für ihre Plattformen und Dienste zu finden.

Dieser Leitfaden soll diese Lücke füllen und konzentriert sich auf Aspekte des virtuellen WebAuthn-Authenticators, die in seiner offiziellen Dokumentation nicht gründlich behandelt werden. Zum Beispiel besprechen wir Konfigurationsoptionen für den virtuellen Authenticator, die in der Dokumentation nicht selbsterklärend sind, sowie Workarounds für bestimmte Anwendungsfälle, für die der virtuelle Authenticator keine bequeme Lösung bietet. Ansonsten ist dieser Leitfaden auch für Entwickler hilfreich, die einfach nach leicht verständlichen Beispielen für die Verwendung des virtuellen Authenticators im Testcode suchen.

Unser Leitfaden verwendet Beispiele aus Playwright, um eine einfache Anleitung zum effektiven Testen der Passkey-Implementierung in Ihrem Projekt zu geben. Playwright ist ein End-to-End (E2E)-Testframework, das das Chrome DevTools Protocol (CDP) als Protokoll für die Browser-Automatisierung verwendet. Wenn Sie speziell nach technischen Beispielen für das Testen von Passkeys in Playwright suchen, können Sie direkt zu Abschnitt 5 springen. Wenn Sie hingegen andere E2E-Testframeworks wie Puppeteer oder Selenium verwenden und Passkeys auf diesen Frameworks testen möchten, werden die Testcode-Implementierungen identisch oder sehr ähnlich zu den in diesem Leitfaden bereitgestellten Beispielen sein, je nachdem, welches Framework Sie verwenden. Im nächsten Abschnitt geben wir einen Überblick über die verschiedenen E2E-Frameworks und wie relevant dieser Leitfaden für diese Frameworks sein wird.

2. Hintergrund: Browser-Automatisierung und E2E-Testframeworks#

2.1. Was ist Browser-Automatisierung?#

Browser-Automatisierung ist, wie der Name schon sagt, der Prozess der Automatisierung sich wiederholender Benutzeraktionen im Browser zum Zweck des Web-Scrapings von Daten oder, in unserem Fall, zum Testen von Webanwendungen. WebDriver und das Chrome DevTools Protocol (CDP) sind zwei der wichtigsten Browser-Automatisierungsprotokolle, die für diesen Leitfaden relevant sind, da sie jeweils eine Implementierung des virtuellen WebAuthn-Authenticators bereitstellen.

2.2. Was ist WebDriver?#

WebDriver ist eine ferngesteuerte Schnittstelle, die als Vermittler in der Kommunikation zwischen dem Client und dem Browser angesehen werden kann. Der Fokus dieses Protokolls liegt darauf, eine plattform- und sprachneutrale Schnittstelle bereitzustellen, die alle wichtigen Browser unterstützt, einschließlich solcher, die nicht auf Chromium basieren, wie Firefox und Safari. Da die WebDriver-Schnittstelle eine Verbindung sowohl mit dem Client als auch mit dem Browser verwalten muss, opfert dieser Ansatz Geschwindigkeit und Stabilität zugunsten einer breiteren Browser-Unterstützung (d.h. höhere Instabilität/Flakiness). Zu den namhaften WebDriver-Clients gehören Selenium und Nightwatch.

Entnommen von jankaritech

2.3. Was ist das Chrome DevTools Protocol (CDP)?#

Das Chrome DevTools Protocol (CDP) hingegen hat keinen Vermittler wie die WebDriver-Schnittstelle zwischen dem Client und dem Browser. Zusätzlich erfolgt die Kommunikation zwischen dem Client und dem Browser über eine Socket-Verbindung, im Gegensatz zur langsameren HTTP-Verbindung zwischen dem Client und der WebDriver-Schnittstelle im vorherigen Ansatz. Diese Punkte machen CDP viel schneller und weniger instabil als WebDriver. Der Nachteil ist, dass dieses Protokoll nur für Chromium-basierte Browser wie Chrome und Edge unterstützt wird. Playwright und Puppeteer sind Beispiel-Clients, die CDP zur Kommunikation mit Browsern verwenden.

Entnommen von jankaritech

2.4. Puppeteer & Playwright als CDP-basierte E2E-Testframeworks#

Puppeteer ist, ähnlich wie Playwright, ein E2E-Framework, das direkt auf dem CDP aufbaut. Das bedeutet, dass Puppeteer und Playwright beide dieselbe Implementierung des virtuellen WebAuthn-Authenticators verwenden und dass die API-Kommunikation über den virtuellen WebAuthn-Authenticator via Socket-Verbindung ebenfalls identisch ist.

Um dies zu demonstrieren, vergleichen wir den Testcode in Playwright und Puppeteer für den Aufruf der getCredentials-Methode, die eine Liste aller bisher im virtuellen Authenticator registrierten Anmeldedaten zurückgibt. Wir fügen auch einen einfachen Event-Listener für das credentialAdded-Ereignis hinzu, das ausgelöst wird, wenn ein Passkey-Anmeldedatensatz erfolgreich registriert wird. Lassen Sie sich nicht von den Details der Implementierung einschüchtern, da diese in den späteren Abschnitten erklärt werden. Diese Beispiele sollen lediglich zeigen, wie ähnlich die Implementierungen zwischen den beiden Frameworks sind.

Ben Gould Testimonial

Ben Gould

Head of Engineering

I’ve built hundreds of integrations in my time, including quite a few with identity providers and I’ve never been so impressed with a developer experience as I have been with Corbado.

10,000+ devs trust Corbado & make the Internet safer with passkeys. Got questions? We've written 150+ blog posts on passkeys.

Join Passkeys Community

Playwright:

const client = await page.context().newCDPSession(page); await client.send('WebAuthn.enable'); const authenticatorId = const result = await client.send('WebAuthn.addVirtualAuthenticator', { options }); ... // get all credentials registered in the virtual authenticator const result = await client.send('WebAuthn.getCredentials', { authenticatorId }); console.log(result.credentials); // add a listener to the credentialAdded event to output a log to the console whenever a passkey credential is registered client.on('WebAuthn.credentialAdded', () => { console.log('Credential Added!'); });

Puppeteer:

const client = await page.target().createCDPSession(); await client.send('WebAuthn.enable'); const authenticatorId = const result = await client.send('WebAuthn.addVirtualAuthenticator', { options }); ... // get all credentials registered in the virtual authenticator const result = await client.send('WebAuthn.getCredentials', { authenticatorId }); console.log(result.credentials); // add a listener to the credentialAdded event to output a log to the console whenever a passkey credential is registered client.on('WebAuthn.credentialAdded', () => { console.log('Credential Added!'); });

Obwohl die Methoden zur Initialisierung der CDP-Sitzung am Anfang der Testcodes leicht unterschiedlich waren, ist der Aufruf von Methoden und die Behandlung von Ereignissen in der CDP WebAuthn Virtual Authenticator API identisch. Das bedeutet, wenn Sie den virtuellen WebAuthn-Authenticator in Puppeteer verwenden möchten, können Sie diesem Leitfaden Zeile für Zeile folgen.

Slack Icon

Become part of our Passkeys Community for updates & support.

Join

2.5. Selenium & Nightwatch als WebDriver-basierte E2E-Testframeworks#

Selenium und Nightwatch sind E2E-Testframeworks, die sich auf WebDriver für die Browser-Automatisierung stützen. Obwohl die Implementierung des virtuellen WebAuthn-Authenticators für WebDriver von seiner Implementierung für CDP getrennt ist, sind ihre API-Spezifikationen ähnlich. Für fast jede Methode in der CDP WebAuthn Virtual Authenticator API finden Sie eine entsprechende Methode in der WebDriver WebAuthn Virtual Authenticator API. Es ist jedoch zu beachten, dass es zwar möglich war, Event-Listener für das erfolgreiche Hinzufügen oder Bestätigen eines Passkeys in der CDP WebAuthn Virtual Authenticator API anzuhängen, dies im WebDriver-Pendant jedoch nicht möglich ist.

Selenium:

const driver = await new Builder().forBrowser('chrome').build(); const options = new VirtualAuthenticatorOptions(); await driver.addVirtualAuthenticator(options); ... // get all credentials registered in the virtual authenticator const credentials = await driver.getCredentials();

Es ist offensichtlich, dass die Syntax zum Einrichten der Instanz des virtuellen Authenticators und zum Tätigen von API-Aufrufen sich von der entsprechenden CDP-Implementierung unterscheidet. Da die API-Spezifikationen der beiden virtuellen WebAuthn-Authenticators jedoch sehr ähnlich sind, wäre es möglich, diesem Leitfaden zu folgen, um eine entsprechende Implementierung auf einem WebDriver-basierten E2E-Testframework zu schreiben.

2.6. Cypress als E2E-Testframework mit nativem Scripting#

Cypress ist ein E2E-Testframework, das nicht primär auf WebDriver oder CDP aufbaut wie die oben genannten Frameworks. Es verwendet natives JavaScript, um mit dem Browser zu kommunizieren. Es bietet jedoch einen Low-Level-Zugriff auf das CDP, was bedeutet, dass es möglich ist, rohe CDP-Befehle zu senden, um den virtuellen WebAuthn-Authenticator des CDP zu nutzen.

Substack Icon

Subscribe to our Passkeys Substack for the latest news.

Subscribe

Da die Syntax für diesen Low-Level-Zugriff umständlich und sehr unterschiedlich zu den obigen Beispielen ist, werden wir in diesem Leitfaden nicht ins Detail gehen. Weitere Informationen zum Aufrufen von CDP-Befehlen in Cypress werden jedoch in diesem Leitfaden erklärt. Die übergeordneten Konzepte zur Verwendung des virtuellen CDP WebAuthn-Authenticators, die in diesem Leitfaden vorgestellt werden, sind dennoch für diejenigen relevant, die Passkeys auf Cypress testen möchten.

3. Was macht E2E-Tests von Passkeys mit Playwright zu einem Problem?#

Es gibt viele Gründe, warum das Testen der Passkey-Implementierung von Natur aus anspruchsvoller ist als andere, einfachere Benutzeraktionen in einer Webumgebung. Die Notwendigkeit, dynamische Benutzerinteraktionen im Zusammenhang mit der biometrischen Authentifizierung zu handhaben, wie z.B. das Scannen von Fingerabdrücken oder die Gesichtserkennung, fügt eine Komplexitätsebene hinzu, die beim Schreiben von Tests möglicherweise nicht im Detail praktisch zu bewältigen ist. Da Sicherheit im Kontext der Authentifizierung naturgemäß ein Hauptanliegen ist, ist es auch notwendig sicherzustellen, dass die Passkey-Authentifizierung nahtlos über verschiedene Browser und Geräte hinweg integriert ist, ohne Raum für Sicherheits-Schwachstellen zu lassen.

4. Der virtuelle WebAuthn-Authenticator ermöglicht E2E-Passkey-Tests#

Die Komplexität der Handhabung dynamischer Benutzerinteraktionen bei Passkey-Operationen sowie das Testen ihrer Integration in verschiedene Browser und Geräte wird durch die Verwendung des virtuellen WebAuthn-Authenticators erleichtert.

4.1. Was ist der virtuelle WebAuthn-Authenticator?#

Der virtuelle WebAuthn-Authenticator ist eine softwarebasierte Darstellung des im WebAuthn-Standard spezifizierten Authenticator-Modells. Er emuliert das Verhalten eines physischen Authenticator-Geräts, wie z.B. eines Hardware-Sicherheitsschlüssels (z.B. YubiKey) oder eines biometrischen Scanners (z.B. bei Face ID, Touch ID oder Windows Hello), arbeitet aber vollständig in der Software (es findet also keine physische Authentifizierung oder das Scannen von Biometrie statt).

4.2. Was sind die Vorteile des virtuellen WebAuthn-Authenticators?#

Es gibt zwei Hauptvorteile des virtuellen WebAuthn-Authenticators.

4.2.1. Automatisierte Tests mit dem virtuellen WebAuthn-Authenticator#

Da WebDriver und CDP Werkzeuge zur Browser-Automatisierung sind, ist es offensichtlich, dass der primäre Anwendungsfall der Implementierung des virtuellen WebAuthn-Authenticators in diesen Protokollen das automatisierte Testen ist. Durch die Nutzung dieser Protokolle ermöglicht der virtuelle Authenticator einfache, aber umfassende Tests von Passkey-Funktionalitäten in kontrollierten Umgebungen wie E2E-Testframeworks (z.B. Playwright, Cypress, Nightwatch).

4.2.2. Manuelle Tests und Demonstration mit dem virtuellen WebAuthn-Authenticator#

Der virtuelle WebAuthn-Authenticator des CDP ist auch über die DevTools des Chrome-Browsers zugänglich und kann für manuelle Tests oder einfach zu Demonstrationszwecken verwendet werden. Mit dieser Funktion können Sie die Passkey-Eingabe auf einem Gerät simulieren, das Passkeys nicht nativ unterstützt. Entsprechend ist es auch möglich, eine nicht Passkey-fähige Umgebung auf einem Gerät zu simulieren, das Passkeys unterstützt.

Der obige Screenshot zeigt ein Beispiel für die Verwendung des virtuellen Authenticators in Chrome für manuelle Tests oder Demonstrationszwecke. Sie können sehen, dass verschiedene Konfigurationsoptionen für den virtuellen Authenticator möglich sind und das Hinzufügen und Löschen von Anmeldedaten ebenfalls verfolgt werden kann. Beziehen Sie sich auf diesen Leitfaden von Google für weitere Informationen zur Verwendung des virtuellen Authenticators in Ihrem Browser, einschließlich der Konfigurationsoptionen und empfohlenen Werte für jede.

4.3. Was sind die Nachteile des virtuellen WebAuthn-Authenticators?#

Obwohl der virtuelle WebAuthn-Authenticator eine elegante Lösung für das Testen von Passkey-Implementierungen ist, gibt es einige Nachteile, die erwähnenswert sind.

4.3.1. Unfähigkeit, hardwarespezifische Funktionalitäten zu simulieren#

Als rein softwarebasierte Lösung kann der virtuelle WebAuthn-Authenticator die einzigartigen Hardware-Eigenschaften und Sicherheitsmerkmale physischer Authenticators nicht nachbilden. Die Unterscheidung zwischen der Verwendung verschiedener Plattform-Authenticators (die in ein Gerät integriert sind, wie z.B. ein biometrischer Scanner auf einem Smartphone) und verschiedener plattformübergreifender Authenticators (die externe Geräte sind, wie Hardware-Sicherheitsschlüssel) kann mit dem virtuellen WebAuthn-Authenticator nicht simuliert werden. Obwohl die Black-Box-Vereinfachung der Komplexität, die mit verschiedenen Arten von Plattform- und plattformübergreifenden Authenticators verbunden ist, einer der Vorteile der Verwendung des virtuellen WebAuthn-Authenticators ist, sollten andere Lösungen in Betracht gezogen werden, wenn Sie die Nuancen der verschiedenen Authenticator-Typen simulieren und testen möchten.

4.3.2. Spärliche Dokumentation und ungelöste technische Probleme#

Angesichts der relativ neuen Einführung von WebAuthn und der Neuheit der Passkey-Technologie reift das Ökosystem um virtuelle Authenticators noch. Dies führt zu einem Mangel an umfassender Dokumentation und ungelösten technischen Herausforderungen, insbesondere im Kontext der Integration virtueller Authenticators in automatisierte Testframeworks. Dieser Leitfaden zielt darauf ab, dieses Problem anzugehen, indem er umfassende Einblicke in das Testen von Passkeys in einer automatisierten Testumgebung bietet und sich gleichzeitig darauf konzentriert, die noch vorhandenen Unannehmlichkeiten bei der Verwendung dieser Tools anzusprechen und Workarounds für diese Probleme zu präsentieren.

5. Wie man den virtuellen WebAuthn-Authenticator in Playwright einrichtet#

Nach einer erfolgreichen Installation von Playwright und seinen Abhängigkeiten können Sie sofort mit dem Schreiben Ihres ersten Tests beginnen, indem Sie eine Datei mit einem Namen erstellen, der auf .spec.ts oder .test.ts endet, mit folgendem Inhalt:

import { test, expect } from "@playwright/test"; test("my first test", async ({ page }) => { await page.goto("https://passkeys.eu"); // start simulating user actions });

Um den virtuellen WebAuthn-Authenticator in Playwright zu verwenden, genügt es, zu Beginn eines Testfalls eine CDP-Sitzung zu initiieren und einen virtuellen Authenticator anzuhängen, wie folgt:

test('signup with passkey', async ({ page }) => { // Initialize a CDP session for the current page const client = await page.context().newCDPSession(page); // Enable WebAuthn environment in this session await client.send('WebAuthn.enable'); // Attach a virtual authenticator with specific options const result = await client.send('WebAuthn.addVirtualAuthenticator', { options: { protocol: 'ctap2', transport: 'internal', hasResidentKey: true, hasUserVerification: true, isUserVerified: true, automaticPresenceSimulation: false, }, }); const authenticatorId = result.authenticatorId; // Further test steps to simulate user interactions and assertions ... });

Optionen zur Konfiguration des virtuellen WebAuthn-Authenticators:

  • protocol: Diese Option gibt das Protokoll an, das der virtuelle Authenticator spricht. Mögliche Werte sind „ctap2“ und „u2f“.
  • transport: Diese Option gibt den Typ des Authenticators an, den der virtuelle Authenticator simuliert. Mögliche Werte sind „usb“, „nfc“, „ble“ und „internal“. Wenn auf „internal“ gesetzt, simuliert er einen Plattform-Authenticator, während andere Werte plattformübergreifende Authenticators simulieren.
  • hasResidentKey: Wenn dies auf true gesetzt ist, wird der Resident Key unterstützt (d.h. clientseitig auffindbare Anmeldeinformationen).
  • hasUserVerification: Wenn dies auf true gesetzt ist, wird die Benutzerverifizierung unterstützt. Dies auf true zu setzen wird empfohlen, da es die Simulation von erfolgreichen und fehlgeschlagenen Passkey-Eingaben ermöglicht.
  • isUserVerified: Wenn dies auf true gesetzt ist, wird ein erfolgreiches Authentifizierungsszenario emuliert, während false einen Authentifizierungsfehler nachahmt, z.B. wenn ein Benutzer die Passkey-Eingabe abbricht. Beachten Sie, dass diese Einstellung nur wirksam ist, wenn hasUserVerification auf true gesetzt ist.
  • automaticPresenceSimulation: Wenn auf true gesetzt, erfolgt die Passkey-Eingabe automatisch und sofort bei jeder Authentifizierungsaufforderung. Umgekehrt erfordert das Setzen auf false die manuelle Initiierung der Passkey-Authentifizierungs-Simulation im Testcode. Die Wahl der manuellen Simulation (false) wird aus zwei Gründen empfohlen:
    • Erhöhung der Lesbarkeit des Testcodes: Die automatische Simulation kann das Verständnis des Testablaufs verschleiern, da Authentifizierungsversuche ohne explizite Auslöser im Testcode simuliert werden.
    • Vermeidung von unbeabsichtigtem Verhalten: Automatische Simulation bedeutet, dass die Passkey-Eingabe auch dann ausgelöst wird, wenn der Tester nicht bemerkt, dass der Passkey angefordert wurde. Dies ist insbesondere ein Problem bei der Conditional UI, die vom Tester leichter übersehen werden könnte.

6. Anwendungsfälle für den virtuellen WebAuthn-Authenticator#

In diesem Abschnitt untersuchen wir die Verwendung der Methoden und Ereignisse des virtuellen WebAuthn-Authenticators im Kontext von sowohl gängigen als auch speziellen Anwendungsfällen.

6.1. Wie man einen Passkey-Eingabeversuch in Playwright simuliert#

Dies ist vielleicht die wichtigste und zugleich verwirrendste Aufgabe bei der Verwendung des virtuellen WebAuthn-Authenticators in einem Testcode, da es keine explizite eingebaute Methode gibt, um eine Passkey-Eingabe auszulösen. Die Lösung liegt in den Konfigurationsoptionen des virtuellen WebAuthn-Authenticators, nämlich isUserVerified und automaticPresenceSimulation. Mit diesen Optionen können wir diese Benutzerinteraktion über zwei verschiedene, unten beschriebene Ansätze simulieren.

6.1.1. Ansatz 1: Automatische Simulation mit automaticPresenceSimulation auf true gesetzt#

Fall 1: Simulation einer erfolgreichen Passkey-Eingabe

test('signup with passkey', async ({ page }) => { ... await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); await page.getByRole('button', { name: 'Login' }).click(); // successful passkey input is automatically simulated (isUserVerified=true) await expect(page.getByRole('heading', { level: 1 })).toHaveText('Welcome!'); ... });

Die Simulation einer erfolgreichen Passkey-Eingabe erfordert normalerweise keine zusätzlichen Zeilen im Testcode. Die letzte Zeile (await expect...) wartet darauf, dass sich die Seite ändert (ausgelöst durch die implizite erfolgreiche Passkey-Eingabe).

Fall 2: Simulation einer abgebrochenen Passkey-Eingabe (die keine Änderungen an der Benutzeroberfläche auslöst)

Das Testen einer fehlgeschlagenen oder abgebrochenen Passkey-Eingabe ist komplizierter, da sie möglicherweise zu keinen beobachtbaren Änderungen an der Benutzeroberfläche führt. Mit anderen Worten, das Warten auf eine Seitenänderung wie im vorherigen Beispiel ist nicht ausreichend, um sicherzustellen, dass die Passkey-Eingabe verarbeitet wurde. Zu prüfen, dass sich die Seite nach der impliziten Passkey-Eingabe nicht geändert hat, ist bedeutungslos, da die Prüfung mit ziemlicher Sicherheit stattfindet, bevor die Passkey-Eingabe abgeschlossen ist. Während der virtuelle Authenticator eine Möglichkeit bietet, auf die Verarbeitung einer erfolgreichen Passkey-Eingabe zu warten, indem auf ein Ereignis gelauscht wird (wie in Ansatz 2 besprochen), gibt es derzeit keine eingebaute Möglichkeit, eine fehlgeschlagene oder abgebrochene Passkey-Eingabe zu erkennen. Ein Workaround wäre, einfach einen festen Timeout hinzuzufügen, um auf den Abschluss der Passkey-Operation zu warten, bevor geprüft wird, dass die Benutzeroberfläche tatsächlich gleich geblieben ist.

test('signup with passkey', async ({ page }) => { // Simulate a set of user actions to trigger a passkey prompt ... await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); // Simulate passkey input when prompted in the test await inputPasskey(async () => { await page.waitForTimeout(300); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); }); // Further test steps ... });

In beiden Fällen ist die Lesbarkeit des Testcodes durch die Implizitheit der Passkey-Operation eingeschränkt. Wie bereits erwähnt, wäre es auch leicht zu übersehen, wenn eine Conditional UI angezeigt wird, in welchem Fall die Passkey-Operation automatisch ohne das Wissen des Testers abgeschlossen würde.

6.1.2. Ansatz 2: Manuelle Simulation mit automaticPresenceSimulation auf false gesetzt#

Das manuelle Auslösen einer Passkey-Eingabe durch Umschalten des Werts der Option automaticPresenceSimulation löst die im vorherigen Ansatz aufgetretenen Probleme, insbesondere in Bezug auf die Lesbarkeit des Testcodes.

Fall 1: Simulation einer erfolgreichen Passkey-Eingabe

Die folgenden Code-Schnipsel simulieren eine erfolgreiche Passkey-Eingabe:

async simulateSuccessfulPasskeyInput(operationTrigger: () => Promise<void>) { // initialize event listeners to wait for a successful passkey input event const operationCompleted = new Promise<void>(resolve => { client.on('WebAuthn.credentialAdded', () => resolve()); client.on('WebAuthn.credentialAsserted', () => resolve()); }); // set isUserVerified option to true // (so that subsequent passkey operations will be successful) await client.send('WebAuthn.setUserVerified', { authenticatorId: authenticatorId, isUserVerified: true, }); // set automaticPresenceSimulation option to true // (so that the virtual authenticator will respond to the next passkey prompt) await client.send('WebAuthn.setAutomaticPresenceSimulation', { authenticatorId: authenticatorId, enabled: true, }); // perform a user action that triggers passkey prompt await operationTrigger(); // wait to receive the event that the passkey was successfully registered or verified await operationCompleted; // set automaticPresenceSimulation option back to false await client.send('WebAuthn.setAutomaticPresenceSimulation', { authenticatorId, enabled: false, }); }
test('signup with passkey', async ({ page }) => { ... // Simulate passkey input with a promise that triggers a passkey prompt as the argument await simulateSuccessfulPasskeyInput(() => page.getByRole('button', { name: 'Create account with passkeys' }).click() ); ... });

Die Hilfsfunktion mag auf den ersten Blick recht einschüchternd wirken. Es hilft zu verstehen, dass alle technischen Komplexitäten der Simulation einer Passkey-Operation in die Hilfsfunktion abstrahiert sind. Das bedeutet, dass der Code bei seiner Verwendung im Testcode einfach und klar wird, wie im zweiten Code-Schnipsel oben zu sehen ist.

Im Vergleich zum impliziten Ansatz in Abschnitt 6.1.1 erhöht dieser explizite Ansatz auch die Lesbarkeit des Codes. Dies wäre besonders hilfreich, wenn eine Conditional UI angezeigt wird, da dieser explizite Ansatz eine unbeabsichtigte, implizite Durchführung der Passkey-Operation ohne das Wissen des Entwicklers verhindert.

Lassen Sie uns nun jeden Teil der Hilfsfunktion verstehen.

Zuerst definieren wir das operationCompleted-Promise, das entweder auf das WebAuthn.credentialAdded-Ereignis oder das WebAuthn.credentialAsserted-Ereignis wartet, die, wie der Name schon sagt, ausgelöst werden, wenn ein Passkey-Anmeldedatensatz registriert bzw. verifiziert wird. Dieses Promise wird später verwendet.

Als Nächstes wird die Option isUserVerified auf true gesetzt, damit die nachfolgende Passkey-Operation durch den virtuellen WebAuthn-Authenticator erfolgreich ist. Die automaticPresenceSimulation wird ebenfalls auf true gesetzt, damit der virtuelle WebAuthn-Authenticator auf die nächste Passkey-Aufforderung von der Webseite reagiert.

Das Warten auf das operationTrigger-Promise ist notwendig, um eine Race Condition zu vermeiden. Die Race Condition tritt auf, wenn die Webseite den Passkey anfordert, bevor automaticPresenceSimulation auf true gesetzt ist. Um dies zu verhindern, muss die Benutzeraktion, die die Passkey-Aufforderung auslöst, ausgeführt werden, nachdem automaticPresenceSimulation auf true gesetzt wurde. Im obigen Beispiel klickt der Benutzer auf die Schaltfläche mit dem Namen „Konto mit Passkeys erstellen“, um die Passkey-Aufforderung auszulösen.

Nachdem die Benutzeraktion abgeschlossen ist, müssen wir auf den Abschluss der erfolgreichen Passkey-Operation warten. Dies geschieht durch Warten auf das Promise, das wir am Anfang der Hilfsfunktion definiert haben. Der Abschluss der erfolgreichen Passkey-Operation wird durch die Auslösung des WebAuthn.credentialAdded- oder WebAuthn.credentialAsserted-Ereignisses markiert. Im obigen Beispiel würde, da der Benutzer einen Passkey registriert, das WebAuthn.credentialAdded-Ereignis ausgelöst.

Schließlich wird die Option automaticPresenceSimulation wieder auf false gesetzt, um zu verhindern, dass später im Testcode unbeabsichtigte Passkey-Operationen stattfinden.

Fall 2: Simulation einer abgebrochenen Passkey-Eingabe

Für eine abgebrochene Passkey-Eingabe müssen wir eine leichte Änderung an der Implementierung für den vorherigen Fall vornehmen. Im Falle einer erfolgreichen Passkey-Eingabe gibt es Ereignisse, nämlich WebAuthn.credentialAdded und WebAuthn.credentialAsserted, die nach Abschluss der Operation ausgelöst werden. Der virtuelle WebAuthn-Authenticator bietet jedoch kein Ereignis für eine abgebrochene oder fehlgeschlagene Passkey-Eingabe. Daher müssen wir einen alternativen Weg finden, um den Abschluss einer abgebrochenen oder fehlgeschlagenen Passkey-Operation zu überprüfen.

Die folgenden Code-Schnipsel simulieren eine fehlgeschlagene Passkey-Eingabe:

async simulateFailedPasskeyInput(operationTrigger: () => Promise<void>, postOperationCheck: () => Promise<void>) { // set isUserVerified option to false // (so that subsequent passkey operations will fail) await client.send('WebAuthn.setUserVerified', { authenticatorId: authenticatorId, isUserVerified: false, }); // set automaticPresenceSimulation option to true // (so that the virtual authenticator will respond to the next passkey prompt) await client.send('WebAuthn.setAutomaticPresenceSimulation', { authenticatorId: authenticatorId, enabled: true, }); // perform a user action that triggers passkey prompt await operationTrigger(); // wait for an expected UI change that indicates the passkey operation has completed await postOperationCheck(); // set automaticPresenceSimulation option back to false await client.send('WebAuthn.setAutomaticPresenceSimulation', { authenticatorId, enabled: false, }); }
test('signup with passkey', async ({ page }) => { // Simulate a set of user actions to trigger a passkey prompt ... await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); // Simulate passkey input when prompted in the test await inputPasskey(async () => { await page.waitForTimeout(300); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); }); // Further test steps ... });

In der Hilfsfunktion werden die Event-Listener durch einen Promise-Parameter postOperationCheck ersetzt, der auf eine erwartete UI-Änderung wartet, bevor automaticPresenceSimulation wieder auf false gesetzt werden kann.

Im Testcode besteht der einzige Unterschied darin, dass die Hilfsfunktion mit einem zusätzlichen Promise aufgerufen werden muss, das auf die beabsichtigte UI-Änderung prüft. Im obigen Beispiel prüfen wir, dass die Webanwendung erfolgreich zu einer Seite navigiert ist, auf der die Überschrift den Text „Etwas ist schiefgelaufen...“ hat.

Wie in Abschnitt 6.1.1 besprochen, führt das Abbrechen einer Passkey-Eingabe möglicherweise zu keiner beobachtbaren Änderung der Benutzeroberfläche. Wie im dortigen Beispiel müssen wir in solchen Fällen eine feste Wartezeit einfügen, bevor wir überprüfen, ob die Benutzeroberfläche tatsächlich unverändert geblieben ist:

test('signup with passkey', async ({ page }) => { ... await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); // Simulate passkey input with a promise that triggers a passkey prompt as the argument await simulateFailedPasskeyInput( () => page.getByRole('button', { name: 'Create account with passkeys' }).click(), async () => { await page.waitForTimeout(300); await expect(page.getByRole('heading', { level: 1 })).toHaveText('Please log in'); } ); ... });

6.2. Wie man die Erstellung von Passkeys testet#

Die Bequemlichkeit der Verwendung eines virtuellen WebAuthn-Authenticators wird durch seine Fähigkeit erhöht, sich bei der Erstellung oder Löschung eines Passkeys durch die Webanwendung wie ein echter Authenticator zu verhalten. Ein Test muss lediglich Benutzeraktionen durchführen, um die Erstellung oder Löschung eines Passkeys in der Webanwendung zu simulieren, und der virtuelle WebAuthn-Authenticator ändert automatisch seine gespeicherten Anmeldeinformationen, ohne dass zusätzlicher Aufwand seitens des Testcodes erforderlich ist.

Hier ist das Beispiel eines Testcodes, der prüft, ob die Webanwendung einen neuen Passkey ordnungsgemäß im Authenticator registriert:

test('signup with passkey', async ({ page }) => { ... // Confirm there are currently no registered credentials const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result1.credentials).toHaveLength(0); // Perform user actions to simulate creation of a passkey credential (e.g. user registration with passkey input) ... // Confirm the passkey was successfully registered const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result2.credentials).toHaveLength(1); ... });

Wenn wir diesen Code-Schnipsel mit den Code-Schnipseln aus Abschnitt 6.1 kombinieren, können wir den Anmeldevorgang auf unserer Demo-Webseite testen. Das folgende Video ist eine Visualisierung des Tests im UI-Modus von Playwright:

6.3. Wie man die Verifizierung von Passkeys testet#

Die Verifizierung eines Passkey-Anmeldedatensatzes mit dem virtuellen WebAuthn-Authenticator funktioniert ähnlich wie die Erstellung eines Passkeys, da der virtuelle Authenticator automatisch die Anzahl der mit einem bestimmten Anmeldedatensatz durchgeführten Verifizierungen nachverfolgt.

test('login with passkey', async ({ page }) => { ... // Confirm there is only one credential, and save its signCount const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result1.credentials).toHaveLength(1); const signCount1 = result1.credentials[0].signCount; // Perform user actions to simulate verification of a passkey credential (e.g. login with passkey input) ... // Confirm the credential's new signCount is greater than the previous signCount const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result2.credentials).toHaveLength(1); expect(result2.credentials[0].signCount).toBeGreaterThan(signCount1); ... });

Das folgende Video demonstriert einen Test für den Login-Ablauf auf unserer Demo-Webseite:

6.4. Wie man das Löschen von Passkeys testet#

Das Löschen eines Passkeys aus einer Webanwendung sollte hingegen keine Informationen im virtuellen WebAuthn-Authenticator ändern. Die Webanwendung sollte nur in der Lage sein, Anmeldedaten zu löschen, die auf ihrem eigenen Server gespeichert sind. Nur der Benutzer selbst sollte in der Lage sein, einen Passkey-Anmeldedatensatz bewusst und manuell aus dem virtuellen WebAuthn-Authenticator zu löschen.

test('delete a registered passkey credential', async ({ page }) => { ... // Confirm there is currently one registered credential const result1 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result1.credentials).toHaveLength(1); // Perform user actions to simulate deletion of a passkey credential ... // Deleting a passkey credential from a website should not remove the credential from the authenticator const result2 = await client.send('WebAuthn.getCredentials', { authenticatorId }); expect(result2.credentials).toHaveLength(1); ... });

Das folgende Video demonstriert einen Test für das Löschen eines Passkey-Anmeldedatensatzes auf unserer Demo-Webseite:

6.5. Wie man die geräteübergreifende Authentifizierung simuliert#

Der intuitivste Weg, eine geräteübergreifende Authentifizierung von einem zweiten Gerät (das noch keinen registrierten Passkey hat) zu simulieren, besteht darin, einfach eine neue Instanz des virtuellen WebAuthn-Authenticators über den CDP-Befehl hinzuzufügen, wie folgt:

test('signup with passkey', async ({ page }) => { ... // add a virtual authenticator for the first device const authenticatorId1 = await client.send('WebAuthn.addVirtualAuthenticator', { options }); // perform test actions of the first device ... // add a virtual authenticator for the second device const authenticatorId2 = await client.send('WebAuthn.addVirtualAuthenticator', { options }); // perform test actions of the second device .. });

Um die Komplexität der Verwaltung der IDs mehrerer virtueller Authenticators zu vermeiden, ist es auch möglich, ein neues Gerät zu simulieren, indem man einfach die Anmeldedaten aus einem einzigen Authenticator löscht und sie bei Bedarf wieder hinzufügt:

test('signup with passkey', async ({ page }) => { ... const result = await client.send('WebAuthn.getCredentials', { authenticatorId }); const credential = result.credentials[0]; // assuming only one registered passkey const credentialId = credential.credentialId; await client.send('WebAuthn.removeCredential', { authenticatorId, credentialId }); // Perform test actions of the second device which doesn't have a registered passkey ... // Call if it's necessary to simulate the first device which has a registered passkey await client.send('WebAuthn.addCredential', { credential }); // Perform test actions of the first device ... });

Dieser Ansatz kann die Implementierung besonders vereinfachen, wenn ein neues Gerät simuliert werden muss, das alte Gerät aber nicht mehr verwendet wird. In diesem Fall müssen Sie lediglich die Anmeldedaten aus dem virtuellen Authenticator löschen und seine Anmeldedaten vollständig verwerfen:

test('signup with passkey', async ({ page }) => { ... const result = await client.send('WebAuthn.getCredentials', { authenticatorId }); const credential = result.credentials[0]; // assuming only one registered passkey const credentialId = credential.credentialId; await client.send('WebAuthn.clearCredentials', { authenticatorId }); // Perform test actions of the second device which doesn't have a registered passkey ... });

7. Alternativen zum virtuellen WebAuthn-Authenticator#

Die Untersuchung von Alternativen zum virtuellen WebAuthn-Authenticator kann Flexibilität bei der Art und Weise bieten, wie Passkey- / WebAuthn-Authentifizierungsprozesse in Projekten getestet werden.

7.1. Testen mit Mock-Services#

Die Entwicklung von Mock-Services oder -Endpunkten kann das Authentifizierungsverhalten effektiv simulieren und Tests vereinfachen, indem die Feinheiten des eigentlichen Authentifizierungsmechanismus abstrahiert werden. Dieser Ansatz ist besonders vorteilhaft, wenn externe Authentifizierungsdienste verwendet werden, da er es ermöglicht, den Fokus auf die Integration und Funktionalität der Systemkomponenten zu legen, ohne sich in die Details der Authentifizierung vertiefen zu müssen.

7.2. Integrationstests mit echten Authenticators#

Für eine gründliche Untersuchung der Authentifizierungsfunktionalitäten bietet der Einsatz echter Authenticators für Integrationstests einen detaillierten Einblick in die Interaktion mit Hardware-Sicherheitsschlüsseln (z.B. YubiKeys) oder biometrischen Geräten (z.B. bei Face ID, Touch ID oder Windows Hello). Obwohl dies aufgrund der komplexen Natur der Integration realer Geräte in automatisierte Tests typischerweise manuell durchgeführt wird, ist es machbar, benutzerdefinierte Automatisierungsskripte zu entwickeln. Diese Skripte können echte Authenticators mit End-to-End-Testframeworks verbinden und bieten eine Annäherung an reale Benutzerszenarien, was die Zuverlässigkeit des Authentifizierungsprozesses in Live-Umgebungen erhöht.

8. Empfehlungen für Entwickler#

Nachdem wir die verschiedenen Optionen demonstriert und spezifische Code-Schnipsel für E2E-Tests von Passkeys / WebAuthn mit Playwright gezeigt haben, möchten wir zusätzlich einige allgemeinere Empfehlungen für Entwickler geben, die neu in diesem Thema sind.

8.1. Studieren Sie die Landschaft der E2E-Testframeworks#

Bevor Sie sich mit dem Testen von Passkeys oder anderen Authentifizierungsmechanismen befassen, ist es wichtig, die verfügbaren E2E-Testframeworks zu bewerten und die für die Anforderungen Ihres Projekts am besten geeignete Option auszuwählen. Berücksichtigen Sie den Kompromiss zwischen der Geschwindigkeit und Stabilität, die von CDP-basierten Frameworks wie Playwright und Puppeteer geboten werden, und der browserübergreifenden Kompatibilität, die von WebDriver-basierten Frameworks wie Selenium und Nightwatch bereitgestellt wird. Während CDP-basierte Frameworks eine schnellere und stabilere Browser-Automatisierung bieten, sind sie auf Chromium-basierte Browser beschränkt. Im Gegensatz dazu bieten WebDriver-basierte Frameworks eine breitere browserübergreifende Kompatibilität, einschließlich Unterstützung für nicht-Chromium-Browser wie Firefox und Safari, wenn auch mit potenziell langsamerer und weniger stabiler Leistung. Das Verständnis dieser Kompromisse hilft Ihnen, eine fundierte Entscheidung zu treffen und das Framework auszuwählen, das am besten zu den Anforderungen Ihres Projekts passt.

8.2. Verstehen Sie die zugrunde liegenden Konzepte von WebAuthn und Passkeys#

Obwohl der virtuelle WebAuthn-Authenticator den Prozess des Testens von Passkey-Implementierungen vereinfacht, ist es für Entwickler entscheidend, ein solides Verständnis der zugrunde liegenden Konzepte des WebAuthn-Standards und von Passkeys zu haben. Machen Sie sich mit den verschiedenen Konfigurationen vertraut, die für den virtuellen WebAuthn-Authenticator verfügbar sind, wie z.B. protocol, transport, hasResidentKey, hasUserVerification und isUserVerified. Das Verständnis dieser Konfigurationen ermöglicht es Ihnen, den virtuellen Authenticator fein abzustimmen, um verschiedene Authentifizierungsszenarien genau zu simulieren. Vertiefen Sie sich außerdem in die Feinheiten der Passkey-Authentifizierung, einschließlich ihrer Integration in verschiedene Browser und Geräte sowie potenzieller Sicherheitsaspekte. Dieses grundlegende Wissen wird Sie befähigen, umfassende und effektive Teststrategien für die Passkey-Authentifizierung in Ihren Webanwendungen zu entwerfen.

9. Zusammenfassung#

Dieser Leitfaden befasste sich mit der Verwendung des virtuellen CDP WebAuthn-Authenticators mit Playwright und beleuchtete fortgeschrittene Konzepte sowie Probleme, die in der offiziellen Dokumentation nicht behandelt werden. Wir haben auch Alternativen zu CDP innerhalb von Playwright und anderen E2E-Testframeworks untersucht. Trotz unterschiedlicher Implementierungen stellen die standardisierten Spezifikationen des virtuellen WebAuthn-Authenticators die Relevanz dieses Leitfadens über verschiedene Web-Automatisierungsprotokolle und End-to-End-Testframeworks hinweg sicher. Um mehr über verschiedene Konzepte im Zusammenhang mit Passkeys zu erfahren, verweisen wir auf unser Glossar relevanter Terminologien, das Ihnen helfen könnte, den virtuellen WebAuthn-Authenticator entsprechend Ihren Bedürfnissen fein abzustimmen.

Add passkeys to your app in <1 hour with our UI components, SDKs & guides.

Start for free

Share this article


LinkedInTwitterFacebook

Enjoyed this read?

🤝 Join our Passkeys Community

Share passkeys implementation tips and get support to free the world from passwords.

🚀 Subscribe to Substack

Get the latest news, strategies, and insights about passkeys sent straight to your inbox.

Related Articles

Table of Contents