Автоматизированное тестирование Webauthn с помощью Playwright
- среда, 12 августа 2026 г. в 00:00:07
Авторизация — это сложно… Хотя казалось бы — самая часто используемая фича в мире. Очень многие процессы завязаны на авторизации и аутентификации. Начиная от ваших любимых соцсетей и заканчивая редакторами кода.
Обычной почты/пароля должно было хватать. А потом у нас появился social login (login via google, yandex, vk, facebook*). А совсем недавно начался тренд на использование 2-х факторной аутентификации, ибо безопасность — ничего не имею против. Я, как и все мы, надеюсь, начал замечать то, что у нас появился ещё один способ авторизации (кроме одноразового кода на телефоне) — через биометрию на вашем компьютере или через специальную флешку. Такая авторизация называется webauthn. Грубо говоря, вставляя такую флешку у друга на компьютере и входя под своими учётными данными (username/password), ваш друг не сможет получить доступ к вашим данным, поскольку флешка-то у вас. Только получив такую флешку лично, можно использовать ваши данные. Если интересно больше почитать про этот способ входа, то коллеги сделали это за меня [1] и [2].
Во-первых, само приложение должно поддерживать U2F [прим. U2F — Universal 2nd Factor, универсальный 2-й фактор] авторизацию.
Во-вторых, чтобы тестировать руками, нужно желательно физически иметь несколько таких флешек. Либо можно использовать биометрию на вашем устройстве (например, на macOS есть сканер отпечатков, а у Windows-ноутбуков есть Windows Hello и сканер, и то не у всех моделей). По итогу в качестве физического способа входа флешка — самый универсальный вариант, и вы можете попросить у вашей организации закупить такие флешки. Ну а ещё в самом DevTools есть специальные возможности для тестирования webauthn. Находятся они в settings -> more tools -> webauthn.

Попав на эту страницу, мы можем создать «виртуальное» устройство для входа.

Ну и далее, при вызове на сайте navigator.credentials.create() мы увидим наше устройство в списке способов входа. И далее, уже при авторизации мы также увидим наше устройство.
Кажется, что сценарий с вставлением флешки не подразумевает автоматизацию как таковую. Однако спешу обрадовать: в Chrome есть соответствующий модуль, который работает с webauthn. И если вы используете Playwright, то вам крупно повезло, и у вас есть несколько опций:
Вызывать webauthn напрямую.
Но в таком случае кода будет очень много: включить webauthn, создать виртуальное устройство, удалить после теста, делать поддержку для разных страниц (если актуально несколько страниц). Плюс на GitHub есть код для webauthn на чистом @playwright/test (кода там много, поэтому в статью не включал, но это не рокет-саенс).
А если у вас playwright>=1.61.0 то у вас есть возможность напрямую взаимодействовать с webauthn через объект credentials. Примеры из официального сайта playwright.dev
// setup test: let the app register a passkey, then save the storage state with it. const context = await browser.newContext(); await context.credentials.install(); const page = await context.newPage(); await page.goto('https://example.com/register'); await page.getByRole('button', { name: 'Create a passkey' }).click(); // Read back the passkey the page registered — it includes the private key. const [credential] = await context.credentials.get({ rpId: 'example.com' }); fs.writeFileSync('playwright/.auth/passkey.json', JSON.stringify(credential));
И далее уже использовать ключ по назначению
// seed the captured passkey so the app starts already enrolled. const credential = JSON.parse(fs.readFileSync('playwright/.auth/passkey.json', 'utf8')); const context = await browser.newContext(); await context.credentials.create(credential.rpId, credential); await context.credentials.install(); const page = await context.newPage(); await page.goto('https://example.com/login'); // navigator.credentials.get() resolves the captured passkey — already signed in.
Использовать готовое решение
playwright-labs/fixture-webauthn [github] [npm] - готовый модуль по работе с webauthn. Код выглядит примерно так:
import { test } from '@playwright-labs/fixture-webauthn'; test('login via passkey', async ( { useWebauthn } ) => { const webauthn = useWebauthn(); await webauthn.enable() // включаем webauthn const authenticator = await webauthn.addVirtualAuthenticator({ protocol: 'ctap2', transport: 'internal', hasResidentKey: true, hasUserVerification: true, isUserVerified: true, }); await page.goto('/login'); // открываем login страницу await page.getByRole('button', { name: 'Sign in with a passkey' }).click(); // клик на кнопку "авторизоваться через ключ доступа" await webauthn.waitForCredentialAsserted(); // ждем окончания авторизации })
Что использовать мне, если есть выбор: На самом деле все просто: если у вас простое приложение, где авторизация это лишь часть чего-то большего, т.е. авторизация через webauthn у вас проходная, то используйте встроенный context.credentials . А в случае, если вам надо работать с конкретным транспортом, или использовать разные независимые authenticators на каждый контекст - то используйте webauthn. Он максимально приближен к вызовам chrome CDP, насколько это возможно. Ниже представлена таблица, касательно того, когда лучше использовать именно fixture-webauthn
Тестируем |
|
|
Конкретный protocol/transport/ctap2Version | ❌ (он зафиксирован) | ✅ |
Симуляцию “пользователь никогда не подствеждает ключ” (automaticPresenceSimulation: false) | ❌ | ✅ |
Значение isUserVerified on/off | ❌ | ✅ |
Имитацию для неверных ответов с подписью (подпись не валидная) | ❌ | ✅ |
Много независимых authenticators для каждого browser context | ❌ (один явный authenticator) | ✅ |
Ожидание событий для waitForCredentialAdded/Asserted/Updated/Deleted | ❌ | ✅ |
Кстати, пару слов по поводу transport и protocol
- protocol - способ авторизации. Может быть u2f или ctap2. U2f - универсальный второй фактор (флешка или биометрия). ctap2 (Client-to-Authenticator Protocol 2) - это новый формат, который еще умеет общаться с hardware чипом шифрования. (поправьте, если не прав)
- transport - способ авторизации и он бывает таких типов: usb | nfc | ble | cable | internal. USB понятно. NFC — это если ваш девайс (компьютер или телефон) поддерживает NFC-передачу между устройствами. BLE — Bluetooth Low Energy (тут, думаю, что понятно). Cable — это в случае проприетарных шнуров и подключения устройства (т. е. не USB, могу ошибаться тут, никогда не сталкивался с таким) через этот [прим. проприетарный] шнур. Internal — это внутренний чип или биометрия. Кстати, есть ещё один способ, который Chrome не поддерживает (возможно, пока не поддерживает), — это Hybrid. Например, через QR-код можно создать ссылку на компьютере, и телефон при сканировании может добавить сразу несколько ключей для другого устройства (например, BLE и биометрию).
В процессе написания статьи у меня возникло чувство дежавю: кажется, что система оплаты в банках делает что-то подобное, что и webauthn. Ведь у нас есть(загибайте пальцы):
qr code (выше уже писалось про QR код)
nfc (бесконтактная оплата)
ble - вжух от сбера [Сбер] [Хабр] [РБК] работает через BLE, поэтому я сюда его включил
smart card(оплата физической картой).
Про Playwright-labs
Этот проект начинался как набор утилит для @playwright/test, чтобы не приходилось переизобретать велосипед, которые я лично использую в проектах, однако он быстро вышел из под контроля разросся до многих незафисимых фич. Среди них есть такие фичи, как:
OTEL - open telemetry (и репортер и fixture для кастомных метрик)
Prometheus - через prometheus remote write протокол
S3 - s3 совместимый движок специально для playwright и фикстур.
Email - email репортер, позволяет создавать свои шаблоны для отправки.
Slack-buildkit - Да, это не готовый репортер(хотя шаблоны есть заготовленные), а именно строительные блоки самого slack block kit, которые вы можете использовать для создания собственных репортеров, в том числе интерактивных репортеров с кнопочками, таблицами и прочим.
QRCode - для чтения\создания qr кодов прямо из теста
Barcode - для чтения\создания bar кодов прямо из теста
SQL - для работы с SQL запросами(есть поддержка автодополнения прямо из кода для ваших таблиц - но это делает отдельный TS plugin)
Lock - Lock сервер для того, чтобы не стучаться в ресурсы одновременно между разными workers(например самый частый случай - у нас общий пользователь, который используется между независимыми workers). Поддерживаются HTTP\WebSocket\IPC\FS транспорты для синхронизации.
Env - строгая валидация для environment variables (есть поддержка окружений: github и gitlab)
Allure - фичи для Аллюра, такие как декораторы для классов и тест фикстур
Selectors-(React/Angular/Vue) - поддержка селекторов для ваших e2e тестов, которая позволяет искать элементы ваших фреимворков на странице(поддерживается props, state и прочее. Но внимание!, не работает для production сборок)
Ну а если было полезно, я предлагаю обсудить, что было бы вам интересно узнать касательно тех фич в playwright-labs, которые проект предоставляет. Или у вас есть идеи, что было бы неплохо добавить в playwright-labs
P.S. эта статья изначально была написана в моем TG канале, но поскольку у меня статьи в канале выходят редко. То оставляю только тем, кто дочитал до конца
*facebook как и кампания meta внесена в список террористических организации на территории РФ