Узнайте, как настроить E2E-тестирование ключей доступа в различных браузерах с помощью Playwright, Nightwatch, Selenium и Puppeteer, используя виртуальный аутентификатор WebAuthn.
Anders
Created: June 17, 2025
Updated: July 8, 2025
See the original blog version in English here.
Наша миссия — сделать Интернет безопаснее, и новый стандарт входа в систему с помощью ключей доступа представляет собой превосходное решение для достижения этой цели. Поэтому мы хотим помочь вам лучше понять ключи доступа и их особенности.
Ключи доступа становятся все более распространенным методом аутентификации, в основе которого лежит стандарт Web Authentication (WebAuthn). Их популярность начала расти совсем недавно, из-за чего документации и других ресурсов по этой теме относительно мало. Это, наряду со сложностью внедрения ключей доступа, может затруднить разработчикам поиск релевантной информации о проектировании, реализации и, особенно, тестировании ключей доступа для своих платформ и сервисов.
Это руководство призвано восполнить этот пробел, уделяя особое внимание аспектам виртуального аутентификатора WebAuthn, которые не полностью освещены в его официальной документации. Например, мы обсуждаем параметры конфигурации виртуального аутентификатора, которые не являются интуитивно понятными из документации, а также обходные пути для некоторых сценариев использования, для которых виртуальный аутентификатор не предоставляет удобного решения. В остальном, это руководство также будет полезно разработчикам, которые просто ищут понятные примеры использования виртуального аутентификатора в тестовом коде.
В нашем руководстве используются примеры на Playwright, чтобы предоставить простое пошаговое руководство по эффективному тестированию реализации ключей доступа в вашем проекте. Playwright — это фреймворк для сквозного (E2E) тестирования, который использует протокол Chrome DevTools (CDP) для автоматизации браузера. Если вас интересуют именно технические примеры тестирования ключей доступа в Playwright, вы можете перейти сразу к разделу 5. С другой стороны, если вы используете другие фреймворки для E2E-тестирования, такие как Puppeteer или Selenium, и хотите протестировать ключи доступа на них, реализация тестового кода будет идентичной или очень похожей на примеры, представленные в этом руководстве, в зависимости от используемого фреймворка. В следующем разделе мы расскажем о различных фреймворках для E2E-тестирования и о том, насколько это руководство будет для них актуально.
Recent Articles
♟️
Как повысить внедрение ключей доступа: лучшие практики для процессов создания
♟️
Создание бизнес-обоснования для ключей доступа: прогнозирование внедрения и ROI
♟️
Как добиться высокого уровня внедрения ключей доступа в процессах входа
🏢
Ключи доступа PayPal: внедряйте ключи доступа, как PayPal
🔑
Лучшие аппаратные ключи безопасности FIDO2 в 2025 году
Автоматизация браузера, как следует из названия, — это процесс автоматизации повторяющихся действий пользователя в браузере с целью сбора данных из веба или, в нашем случае, для тестирования веб-приложений. WebDriver и протокол Chrome DevTools (CDP) — это два основных протокола автоматизации браузера, которые важны для этого руководства, поскольку каждый из них предоставляет реализацию виртуального аутентификатора WebAuthn.
WebDriver — это интерфейс с удаленным управлением, который можно рассматривать как посредника в коммуникации между клиентом и браузером. Основная цель этого протокола — предоставить платформенно- и языково-нейтральный интерфейс, поддерживающий все основные браузеры, включая те, что не основаны на Chromium, такие как Firefox и Safari. Поскольку интерфейсу WebDriver необходимо управлять соединением как с клиентом, так и с браузером, этот подход жертвует скоростью и стабильностью в обмен на более широкий спектр поддержки браузеров (т.е. более высокую вероятность сбоев). Среди известных клиентов WebDriver — Selenium и Nightwatch.
Взято из jankaritech
Протокол Chrome DevTools (CDP), в свою очередь, не имеет посредника, подобного интерфейсу WebDriver, между клиентом и браузером. Кроме того, связь между клиентом и браузером происходит через сокет-соединение, в отличие от более медленного HTTP-соединения между клиентом и интерфейсом WebDriver в предыдущем подходе. Эти моменты делают CDP намного быстрее и менее подверженным сбоям, чем WebDriver. Недостатком является то, что этот протокол поддерживается только для браузеров на базе Chromium, таких как Chrome и Edge. Playwright и Puppeteer — примеры клиентов, которые используют CDP для взаимодействия с браузерами.
Взято из jankaritech
Puppeteer, подобно Playwright, является фреймворком для E2E-тестирования, построенным непосредственно на CDP. Это означает, что и Puppeteer, и Playwright используют одну и ту же реализацию виртуального аутентификатора WebAuthn, и что API-коммуникация с использованием виртуального аутентификатора WebAuthn через сокет-соединение также идентична.
Для демонстрации сравним тестовый код в Playwright и Puppeteer для вызова метода
getCredentials
, который возвращает список всех учетных данных, зарегистрированных в
виртуальном аутентификаторе. Мы также добавим простой обработчик события
credentialAdded
, которое срабатывает при успешной регистрации учетных данных ключа
доступа. Не пугайтесь деталей реализации, они будут объяснены в последующих разделах. Эти
примеры просто демонстрируют, насколько схожи реализации в этих двух фреймворках.
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 CommunityPlaywright:
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!'); });
Хотя методы инициализации сессии CDP в начале тестового кода немного отличались, вызов методов и обработка событий в API виртуального аутентификатора WebAuthn для CDP идентичны. Это означает, что если вы хотите использовать виртуальный аутентификатор WebAuthn в Puppeteer, вы можете следовать этому руководству дословно.
Selenium и Nightwatch — это фреймворки для E2E-тестирования, которые полагаются на WebDriver для автоматизации браузера. Хотя реализация виртуального аутентификатора WebAuthn для WebDriver отделена от его реализации для CDP, их спецификации API схожи. Почти для каждого метода в API виртуального аутентификатора WebAuthn для CDP, вы можете найти соответствующий метод в API виртуального аутентификатора WebAuthn для WebDriver. Однако стоит отметить, что хотя в API виртуального аутентификатора WebAuthn для CDP можно было прикреплять обработчики событий для успешного добавления или подтверждения ключа доступа, в аналоге для WebDriver это невозможно.
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();
Очевидно, что синтаксис настройки экземпляра виртуального аутентификатора и выполнения вызовов API отличается от соответствующей реализации для CDP. Однако, поскольку спецификации API двух виртуальных аутентификаторов WebAuthn очень похожи, вполне возможно следовать этому руководству для написания соответствующей реализации на фреймворке для E2E-тестирования на основе WebDriver.
Cypress — это фреймворк для E2E-тестирования, который не построен в первую очередь на WebDriver или CDP, как упомянутые выше фреймворки. Он использует нативный JavaScript для взаимодействия с браузером. Однако он предоставляет низкоуровневый доступ к CDP, что означает возможность отправлять «сырые» команды CDP для использования виртуального аутентификатора WebAuthn из CDP.
Поскольку синтаксис для этого низкоуровневого доступа утомителен и сильно отличается от приведенных выше примеров, мы не будем вдаваться в подробности в этом руководстве. Однако дополнительную информацию о том, как вызывать команды CDP в Cypress, можно найти в этом руководстве. Общие концепции использования виртуального аутентификатора WebAuthn из CDP, представленные в этом руководстве, по-прежнему актуальны для тех, кто хочет тестировать ключи доступа в Cypress.
Существует множество причин, по которым тестирование реализации ключей доступа по своей природе сложнее, чем другие, более простые действия пользователя в веб-среде. Необходимость обрабатывать динамические взаимодействия с пользователем, связанные с биометрической аутентификацией, такие как сканирование отпечатков пальцев или распознавание лиц, добавляет уровень сложности, который может быть непрактично подробно рассматривать при написании тестов. Поскольку безопасность, естественно, является основной проблемой в контексте аутентификации, также необходимо обеспечить бесшовную интеграцию аутентификации по ключу доступа в различных браузерах и устройствах, не оставляя места для уязвимостей безопасности.
Упрощение сложности обработки динамических взаимодействий с пользователем, связанных с операциями с ключами доступа, а также тестирование их интеграции в различные браузеры и устройства, становится проще с использованием виртуального аутентификатора WebAuthn.
Виртуальный аутентификатор WebAuthn — это программное представление модели аутентификатора, указанной в стандарте WebAuthn. Он эмулирует поведение физического устройства-аутентификатора, такого как аппаратный ключ безопасности (например, YubiKey) или биометрический сканер (например, используемый в Face ID, Touch ID или Windows Hello), но работает полностью программно (то есть без физической аутентификации или сканирования биометрии).
Существует два основных преимущества виртуального аутентификатора WebAuthn.
Поскольку WebDriver и CDP являются инструментами автоматизации браузера, очевидно, что основной сценарий использования реализации виртуального аутентификатора WebAuthn в этих протоколах — это автоматизированное тестирование. Используя эти протоколы, виртуальный аутентификатор позволяет проводить простое, но всестороннее тестирование функциональности ключей доступа в контролируемых средах, таких как фреймворки для E2E-тестирования (например, Playwright, Cypress, Nightwatch).
Виртуальный аутентификатор WebAuthn из CDP также доступен через DevTools браузера Chrome и может использоваться для ручного тестирования или просто в демонстрационных целях. С помощью этой функции вы можете симулировать ввод ключа доступа на устройстве, которое нативно не поддерживает ключи доступа. Соответственно, также возможно симулировать среду без поддержки ключей доступа на устройстве, которое их поддерживает.
На скриншоте выше показан пример использования виртуального аутентификатора в Chrome для ручного тестирования или демонстрационных целей. Вы можете видеть, что возможны различные варианты конфигурации виртуального аутентификатора, а также можно отслеживать добавление и удаление учетных данных. Обратитесь к этому руководству от Google для получения дополнительной информации об использовании виртуального аутентификатора в вашем браузере, включая параметры конфигурации и рекомендуемые значения для каждого из них.
Хотя виртуальный аутентификатор WebAuthn является элегантным решением для тестирования реализаций ключей доступа, стоит отметить несколько недостатков.
Будучи чисто программным решением, виртуальный аутентификатор WebAuthn не может воспроизвести уникальные аппаратные характеристики и функции безопасности физических аутентификаторов. Различие между использованием различных платформенных аутентификаторов (встроенных в устройство, таких как биометрический сканер на смартфоне) и различных кросс-платформенных аутентификаторов (внешних устройств, таких как аппаратные ключи безопасности) не может быть симулировано с помощью виртуального аутентификатора WebAuthn. Хотя упрощение сложностей, связанных с различными типами платформенных и кросс-платформенных аутентификаторов, является одним из преимуществ использования виртуального аутентификатора WebAuthn, если вы хотите симулировать и тестировать нюансы различных типов аутентификаторов, следует изучить другие решения.
Учитывая относительно недавнее внедрение WebAuthn и новизну технологии ключей доступа, экосистема вокруг виртуальных аутентификаторов все еще находится в стадии развития. Это приводит к нехватке исчерпывающей документации и нерешенным техническим проблемам, особенно в контексте интеграции виртуальных аутентификаторов с фреймворками для автоматизированного тестирования. Это руководство призвано решить эту проблему, предоставляя всесторонние сведения о тестировании ключей доступа в среде автоматизированного тестирования, а также уделяя внимание неудобствам, все еще присутствующим при использовании этих инструментов, и представляя обходные пути для этих проблем.
После успешной установки Playwright и его зависимостей вы можете сразу же приступить к написанию своего первого теста, создав файл с именем, оканчивающимся на .spec.ts или .test.ts, со следующим содержимым:
import { test, expect } from "@playwright/test"; test("my first test", async ({ page }) => { await page.goto("https://passkeys.eu"); // start simulating user actions });
Чтобы использовать виртуальный аутентификатор WebAuthn в Playwright, достаточно просто инициировать сессию CDP и подключить виртуальный аутентификатор в начале тестового случая, как показано ниже:
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 ... });
Параметры для настройки виртуального аутентификатора WebAuthn:
true
обеспечивает поддержку
Resident Key (т.е.
обнаруживаемых учетных данных
на стороне клиента).true
обеспечивает поддержку
проверки пользователя. Рекомендуется устанавливать
это значение в true
, так как это позволяет симулировать успешный и неудачный ввод
ключа доступа.true
эмулирует успешный сценарий аутентификации, тогда
как false
имитирует сбой аутентификации, например, когда пользователь отменяет ввод
ключа доступа. Обратите внимание, что этот параметр действует только тогда, когда
hasUserVerification
установлено в true
.true
, ввод ключа доступа
происходит автоматически и немедленно при любом запросе на аутентификацию. И наоборот,
установка в false
требует ручной инициации симуляции
аутентификации по ключу доступа в тестовом коде.
Выбор ручной симуляции (false
) рекомендуется по двум причинам:
В этом разделе мы рассмотрим использование методов и событий виртуального аутентификатора WebAuthn в контексте как распространенных, так и редких сценариев использования.
Это, возможно, самая важная и в то же время запутанная задача при использовании
виртуального аутентификатора WebAuthn в тестовом коде, поскольку нет явного встроенного
метода для запуска ввода ключа доступа. Решение кроется в параметрах конфигурации
виртуального аутентификатора WebAuthn, а именно isUserVerified
и
automaticPresenceSimulation
. С помощью этих параметров мы можем симулировать это
взаимодействие с пользователем двумя различными подходами, описанными ниже.
automaticPresenceSimulation
установленным в true
#Случай 1: Симуляция успешного ввода ключа доступа
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!'); ... });
Симуляция успешного ввода ключа доступа обычно не требует дополнительных строк в тестовом
коде. Последняя строка (await expect...
) ожидает изменения страницы (вызванного неявным
успешным вводом ключа доступа).
Случай 2: Симуляция отмененного ввода ключа доступа (который не вызывает изменений в UI)
Тестирование неудачного или отмененного ввода ключа доступа сложнее, так как это может не привести к каким-либо видимым изменениям в UI. Другими словами, ожидание изменения страницы, как в предыдущем примере, недостаточно для того, чтобы убедиться, что обработка ввода ключа доступа завершена. Проверка того, что страница не изменилась после неявного ввода ключа доступа, бессмысленна, так как проверка почти наверняка произойдет до завершения обработки ввода ключа доступа. Хотя виртуальный аутентификатор предоставляет способ дождаться обработки успешного ввода ключа доступа, прослушивая событие (как будет обсуждаться в подходе 2), в настоящее время нет встроенного способа обнаружить неудачный или отмененный ввод ключа доступа. Обходным путем было бы просто добавить жесткую задержку, чтобы дождаться завершения операции с ключом доступа, прежде чем проверять, что UI действительно остался прежним.
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 ... });
В любом случае, читаемость тестового кода ограничена неявностью операции с ключом доступа. Как упоминалось ранее, также легко пропустить, когда может быть запрошен условный UI, и в этом случае операция с ключом доступа автоматически завершится без ведома тестировщика.
automaticPresenceSimulation
установленным в false
#Ручной запуск ввода ключа доступа путем переключения значения опции
automaticPresenceSimulation
решает проблемы, с которыми мы столкнулись в предыдущем
подходе, а именно с точки зрения читаемости тестового кода.
Случай 1: Симуляция успешного ввода ключа доступа
Следующие фрагменты кода симулируют успешный ввод ключа доступа:
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() ); ... });
Вспомогательная функция может показаться довольно пугающей на первый взгляд. Важно понимать, что все технические сложности симуляции операции с ключом доступа абстрагированы во вспомогательную функцию. Это означает, что при ее использовании в тестовом коде она делает код простым и понятным, как видно из второго фрагмента кода выше.
По сравнению с неявным подходом из раздела 6.1.1, этот явный подход также повышает читаемость кода. Это будет особенно полезно, когда запрашивается условный UI, так как этот явный подход предотвращает непреднамеренное, неявное завершение операции с ключом доступа без ведома разработчика.
Теперь давайте разберем каждую часть вспомогательной функции.
Сначала мы определяем промис operationCompleted
, который ожидает либо события
WebAuthn.credentialAdded
, либо WebAuthn.credentialAsserted
, которые, как следует из
названия, генерируются при регистрации или проверке учетных данных ключа доступа
соответственно. Этот промис будет использован позже.
Далее, опция isUserVerified
устанавливается в true
, чтобы последующая операция с
ключом доступа виртуальным аутентификатором WebAuthn была успешной.
automaticPresenceSimulation
также устанавливается в true
, чтобы виртуальный
аутентификатор WebAuthn ответил на следующий запрос ключа доступа от веб-страницы.
Ожидание промиса operationTrigger
необходимо, чтобы избежать состояния гонки. Состояние
гонки возникает, когда веб-страница запрашивает ключ доступа до того, как
automaticPresenceSimulation
будет установлено в true
. Чтобы предотвратить это,
действие пользователя, которое вызывает запрос ключа доступа, должно быть выполнено после
установки automaticPresenceSimulation
в true
. В приведенном выше примере пользователь
нажимает кнопку с именем «Create account with passkeys», чтобы вызвать запрос ключа
доступа.
После завершения действия пользователя мы должны дождаться завершения успешной операции с
ключом доступа. Это делается путем ожидания промиса, который мы определили в начале
вспомогательной функции. Завершение успешной операции с ключом доступа отмечается
генерацией события WebAuthn.credentialAdded
или WebAuthn.credentialAsserted
. В
приведенном выше примере, поскольку пользователь регистрирует ключ доступа, будет
сгенерировано событие WebAuthn.credentialAdded
.
Наконец, опция automaticPresenceSimulation
устанавливается обратно в false
, чтобы
предотвратить непреднамеренные операции с ключами доступа позже в тестовом коде.
Случай 2: Симуляция отмененного ввода ключа доступа
Для отмененного ввода ключа доступа мы должны внести небольшое изменение в реализацию для
предыдущего случая. В случае успешного ввода ключа доступа есть события, а именно
WebAuthn.credentialAdded
и WebAuthn.credentialAsserted
, которые генерируются по
завершении операции. Однако виртуальный аутентификатор WebAuthn не предоставляет никакого
события для отмененного или неудачного ввода ключа доступа. Таким образом, мы должны
использовать альтернативный способ проверки завершения отмененной или неудачной операции с
ключом доступа.
Следующие фрагменты кода симулируют неудачный ввод ключа доступа:
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 ... });
Во вспомогательной функции обработчики событий заменены параметром-промисом
postOperationCheck
, который ожидает ожидаемого изменения в UI, прежде чем
automaticPresenceSimulation
можно будет установить обратно в false
.
В тестовом коде единственное отличие заключается в том, что вспомогательную функцию нужно вызывать с дополнительным промисом, который проверяет предполагаемое изменение в UI. В приведенном выше примере мы проверяем, что веб-приложение успешно перешло на страницу, где заголовок имеет текст «Something went wrong...».
Как обсуждалось в разделе 6.1.1, отмена ввода ключа доступа может не привести к каким-либо видимым изменениям в UI. Как и в примере, приведенном в том разделе, мы должны добавить жесткое ожидание перед проверкой того, что UI действительно остался прежним в таких случаях:
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'); } ); ... });
Удобство использования виртуального аутентификатора WebAuthn усиливается его способностью вести себя как настоящий аутентификатор в случае создания или удаления ключа доступа веб-приложением. Тесту просто нужно выполнить действия пользователя для симуляции создания или удаления ключа доступа в веб-приложении, и виртуальный аутентификатор WebAuthn автоматически изменяет сохраненную информацию об учетных данных без какой-либо дополнительной работы со стороны тестового кода.
Вот пример тестового кода, который проверяет, что веб-приложение правильно регистрирует новый ключ доступа в аутентификаторе:
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); ... });
Объединив этот фрагмент кода с фрагментами из раздела 6.1, мы можем протестировать процесс регистрации на нашей демо-странице. Следующее видео — это визуализация теста в режиме UI Playwright:
Проверка учетных данных ключа доступа с помощью виртуального аутентификатора WebAuthn работает аналогично созданию ключа доступа, в том смысле, что виртуальный аутентификатор автоматически отслеживает количество проверок, выполненных с использованием определенных учетных данных.
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); ... });
Следующее видео демонстрирует тест для процесса входа в систему на нашей демо-странице:
Удаление ключа доступа из веб-приложения, с другой стороны, не должно изменять никакой информации в виртуальном аутентификаторе WebAuthn. Веб-приложение должно иметь возможность удалять только учетные данные, сохраненные на его собственном сервере. Только сам пользователь должен иметь возможность сознательно и вручную удалить учетные данные ключа доступа из виртуального аутентификатора WebAuthn.
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); ... });
Следующее видео демонстрирует тест на удаление учетных данных ключа доступа на нашей демо-странице:
Самый интуитивный способ симулировать кросс-девайсную аутентификацию со второго устройства (на котором еще нет зарегистрированного ключа доступа) — это просто добавить новый экземпляр виртуального аутентификатора WebAuthn через команду CDP, вот так:
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 .. });
Чтобы избежать сложности управления идентификаторами нескольких виртуальных аутентификаторов, также можно симулировать новое устройство, просто удалив учетные данные из одного аутентификатора и добавив их обратно при необходимости:
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 ... });
Этот подход может особенно упростить реализацию в случае, когда необходимо симулировать новое устройство, а старое устройство больше не нужно использовать. В этом случае вам просто нужно очистить учетные данные из виртуального аутентификатора и полностью их отбросить:
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 ... });
Изучение альтернатив виртуальному аутентификатору WebAuthn может предложить гибкость в том, как процессы аутентификации с помощью ключей доступа / WebAuthn тестируются в проектах.
Разработка mock-сервисов или эндпоинтов может эффективно симулировать поведение аутентификации, упрощая тесты за счет абстрагирования от сложностей реального механизма аутентификации. Этот подход особенно полезен при использовании внешних сервисов аутентификации, позволяя сосредоточиться на интеграции и функциональности компонентов системы, не углубляясь в специфику аутентификации.
Для тщательной проверки функциональности аутентификации использование реальных аутентификаторов для интеграционного тестирования дает детальное представление о взаимодействии с аппаратными ключами безопасности (например, YubiKeys) или биометрическими устройствами (например, используемыми в Face ID, Touch ID или Windows Hello). Хотя это обычно проводится вручную из-за сложной природы интеграции реальных устройств в автоматизированные тесты, возможно разработать пользовательские скрипты автоматизации. Эти скрипты могут соединить реальные аутентификаторы с фреймворками для сквозного тестирования, предлагая более близкое приближение к реальным сценариям пользователя и повышая надежность процесса аутентификации в реальных средах.
После демонстрации различных вариантов и показа конкретных фрагментов кода для E2E-тестирования ключей доступа / WebAuthn с помощью Playwright, мы дополнительно хотим предоставить некоторые более общие рекомендации для разработчиков, новых в этой теме.
Прежде чем погружаться в тестирование ключей доступа или любых других механизмов аутентификации, важно оценить доступные фреймворки для E2E-тестирования и выбрать наиболее подходящий вариант в соответствии с требованиями вашего проекта. Учитывайте компромисс между скоростью и стабильностью, предлагаемыми фреймворками на основе CDP, такими как Playwright и Puppeteer, и кросс-браузерной совместимостью, предоставляемой фреймворками на основе WebDriver, такими как Selenium и Nightwatch. Хотя фреймворки на основе CDP предлагают более быструю и стабильную автоматизацию браузера, они ограничены браузерами на базе Chromium. В отличие от них, фреймворки на основе WebDriver обеспечивают более широкую кросс-браузерную совместимость, включая поддержку браузеров не на базе Chromium, таких как Firefox и Safari, хотя и с потенциально более медленной и менее стабильной производительностью. Понимание этих компромиссов поможет вам принять обоснованное решение и выбрать фреймворк, который наилучшим образом соответствует потребностям вашего проекта.
Хотя виртуальный аутентификатор WebAuthn упрощает процесс тестирования реализаций ключей доступа, разработчикам крайне важно иметь твердое понимание основных концепций, лежащих в основе стандарта WebAuthn и ключей доступа. Ознакомьтесь с различными конфигурациями, доступными для виртуального аутентификатора WebAuthn, такими как protocol, transport, hasResidentKey, hasUserVerification и isUserVerified. Понимание этих конфигураций позволит вам точно настроить виртуальный аутентификатор для точной симуляции различных сценариев аутентификации. Кроме того, углубитесь в тонкости аутентификации по ключу доступа, включая ее интеграцию с различными браузерами и устройствами, а также потенциальные соображения безопасности. Эти фундаментальные знания позволят вам разрабатывать комплексные и эффективные стратегии тестирования для аутентификации по ключу доступа в ваших веб-приложениях.
В этом руководстве мы подробно рассмотрели использование виртуального аутентификатора WebAuthn из CDP с помощью Playwright, осветив продвинутые концепции и решив проблемы, не затронутые в официальной документации. Мы также изучили альтернативы CDP в Playwright и других фреймворках для E2E-тестирования. Несмотря на различные реализации, стандартизированные спецификации виртуального аутентификатора WebAuthn обеспечивают актуальность этого руководства для различных протоколов автоматизации веба и фреймворков сквозного тестирования. Чтобы глубже изучить различные концепции, касающиеся ключей доступа, обратитесь к нашему глоссарию релевантных терминов, который может помочь вам настроить виртуальный аутентификатор WebAuthn в соответствии с вашими потребностями.
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