Pull to refresh
89.6
Skillfactory
Онлайн-школа IT-профессий

Как мы создали вкладку WebAuthn в Chrome DevTools

Reading time8 min
Views2.5K
Original author: Fawaz Mohammad, Nina Satragno
Сегодня, в преддверии старта нового потока курса по JavaScript, делимся с вами полезным переводом статьи о том, как разрабатывалась вкладка WebAuthn в Chrome DevTools, какие решения принимались и почему, с какой проблемой столкнулись разработчики.

image




Web Authentication API, также известный как WebAuthn, позволяет серверам использовать криптографию с открытым ключом (а не пароли) для регистрации и аутентификации пользователей. Этот API включает интеграцию между серверами и сильными аутентификаторами. Эти аутентификаторы могут быть специализированными физическими устройствами (например, ключами безопасности) или они могут иметь интеграцию с платформами (например, считывателями отпечатков пальцев). Прочитать больше о WebAuthn по адресу webauthn.guide.

Проблемы разработчика


До этого проекта в WebAuthn не было встроенной поддержки отладки в Chrome. Разработчику создающему приложение, которое использует WebAuth, требовался физический доступ к реально существующим аутентификаторам. Это было особенно сложно по двум причинам:

  1. Существует множество различных типов аутентификаторов. Для отладки широкого спектра конфигураций и возможностей разработчик должен иметь доступ ко многим, иногда дорогостоящим аутентификаторам.
  2. Физические аутентификаторы защищены по своей природе, поэтому пристально изучить их состояние обычно невозможно.

Нам хотелось упростить задачу, добавив поддержку отладки прямо в Chrome DevTools.

Решение: новая вкладка — WebAuthn


Вкладка WebAuthn DevTools значительно упрощает отладку WebAuthn, позволяя разработчикам эмулировать аутентификаторы, настраивать их возможности и проверять их состояния.



Реализация


Добавление поддержки отладки в WebAuthn состояло из двух частей.



Часть 1. Добавление домена WebAuthn в протокол Chrome DevTools


Во-первых, мы реализовали новый домен в Chrome DevTools Protocol (CDP), который подключается к обработчику, взаимодействующему с бекендом WebAuthn.

CDP объединяет интерфейс DevTools с Chromium. В нашем случае действия домена WebAuthn используются для соединения вкладки WebAuthn DevTools и реализации WebAuthn в Chromium.

Домен WebAuthn позволяет включать и отключать виртуальную среду аутентификатора, которая отключает браузер от реального обнаружения аутентификатора, подключая виртуальное обнаружение.

Мы также предоставляем методы в домене, действующие как тонкий слой к существующим интерфейсам виртуального обнаружения и виртуального аутентификатора, которые являются частью реализации Chromium WebAuthn. Эти методы включают добавление и удаление аутентификаторов, а также создание, получение и удаление их зарегистрированных учетных данных. Посмотрите код.

Хотя домен WebAuthn сделал возможным существование вкладки WebAuthn, это еще не все. Этот домен изначально был большей частью API тестирования WebAuthn и используется ChromeDriver для включения тестов веб-платформы. Узнайте больше о WebAuthn API для тестирования.

Часть 2. Создание ориентированной на пользователя вкладки


Во-вторых, мы создали вкладку, ориентированную на пользователя, в в интерфейсе DevTools. Вкладка состоит из представления и модели. Домен и вкладку соединяет автоматически сгенерированный агент. Хотя неизбежно необходимы три компонента, нужно было уделить внимание только двум из них: модели и представлении. Третий компонент — агент, создается автоматически после добавления домена. Вкратце, агент — это слой, передающий вызовы между фронтендом и CDP.

Модель


Модель — это связывающий агента и представление слой на JavaScript. В нашем случае модель довольно простая. Она принимает команды из представления, формирует запросы так, чтобы они могли использоваться CDP, а затем отправляет их через агента. Эти запросы обычно однонаправленные, при этом никакая информация не отправляется обратно в представление. Однако иногда мы возвращаем ответ от модели либо для предоставления идентификатора вновь созданного аутентификатора, либо для возврата хранящихся в существующем аутентификаторе учетных данных. Посмотрите код.

Представление




Представление используется, чтобы предоставить пользовательский интерфейс, который увидит разработчик, когда откроет DevTools. Этот интерфейс содержит:

  1. Панель инструментов для включения виртуального аутентификатора.
  2. Раздел добавления аутентификаторов.
  3. Раздел для созданных аутентификаторов.

Посмотрите на код.

Панель инструментов для включения среды виртуального аутентификатора



Большинство пользовательских взаимодействий происходит с одним аутентификатором за один раз, а не со всей вкладкой, поэтому единственная функция, которую мы предоставляем на панели инструментов — включение и выключение виртуальной среды.

Зачем это нужно? Это нужно потому, что важно, чтобы пользователь явно переключал среду, потому что переключение отключает браузер от реального обнаружения аутентификаторов. Поэтому, пока браузер включен, подключенные физические аутентификаторы, (например, считыватель отпечатков пальцев), не распознаются.

Мы решили, что явное переключение дает пользователю лучшие впечатления, особенно это верно для тех, кто заходит на вкладку WebAuthn, не ожидая отключения обнаружения реальных устройств.

Добавление раздела аутентификаторов




После включения среды виртуального аутентификатора мы представляем разработчику встроенную форму, позволяющую добавить виртуальный аутентификатор. В этой форме предоставлены параметры настройки, позволяющие пользователю выбирать протокол аутентификатора и методы транспорта, а также то, поддерживает ли аутентификатор резидентные ключи и верификацию пользователя. После нажатия кнопки Add эти параметры объединяются и отправляются модели, которая делает вызов, чтобы создать аутентификатор. Как только вызов завершиться, фронтенд получит ответ, а затем изменит пользовательский интерфейс — появится вновь созданный аутентификатор.

Представление аутентификатора




Каждый раз при эмуляции аутентификатора, в его представление добавляется раздел этого аутентификатора. Каждый такой раздел содержит имя, идентификатор, параметры конфигурации, кнопки для удаления аутентификатора или его активации, а также таблицу учетных данных.

Имя аутентификатора


Имя аутентификатора изменяемо и по умолчанию имеет значение «Authenticator», объединенное с последними 5 символами его ID. Первоначально имя аутентификатора было его полным идентификатором и не могло изменяться. Мы реализовали настраиваемые имена, чтобы пользователь мог маркировать аутентификатор в зависимости от его возможностей, эмулируемого физического аутентификатора, или любого псевдонима, который понять легче, чем идентификатор из 36 символов.

Таблица учетных данных


Мы добавили таблицу в каждый раздел аутентификатора, в которой показаны все зарегистрированные аутентификатором учетные данные. В каждой строке представлена информацию об учетных данных, а также кнопки для их удаления или экспорта. В настоящее время мы собираем информацию для заполнения этих таблиц, опрашивая CDP, чтобы получить зарегистрированные учетные данные для каждого аутентификатора. В будущем планируется добавить событие создания учетных данных.

Кнопка Active

Мы также добавили переключатель Active в каждый раздел аутентификатора. Установленный активным аутентификатор будет единственным прослушивающим и регистрирующим учетные данные. Без этого регистрация учетных данных для нескольких аутентификаторов будет недетерминированной, что при попытке протестировать WebAuthn с их помощью было бы фатальной ошибкой.

Статус Active реализован с помощью метода SetUserPresence на виртуальных аутентификаторах. Метод SetUserPresence устанавливает, успешны ли тесты присутствия пользователя для данного аутентификатора. Если мы отключим его, аутентификатор не сможет прослушивать учетные данные. Следовательно, убедившись, что он включен не более чем для одного аутентификатора (установленного активным), и отключив присутствие пользователя для всех остальных, возможно добиться полной определенности поведения. Интересная проблема, с которой мы столкнулись при добавлении кнопки Active — как избежать состояния гонки. Рассмотрим такую ситуацию:

  1. Пользователь нажимает переключатель Active у аутентификатора X, отправляя запрос к CDP, чтобы установить X в активное состояние. Итак, для X выбран переключатель Active, а все остальные переключатели не выбраны.
  2. Сразу после этого пользователь нажимает переключатель Active аутентификатора Y, отправляя запрос в CDP, чтобы установить Y активным. Для Y установлен переключатель Active, а все остальные, в том числе для X, не выбраны.
  3. На бекенде вызов для установки Y как активного завершается и разрешается. Y теперь активен, а все остальные аутентификаторы — нет.
  4. В серверной части вызов для установки X как активного завершен и разрешен. X теперь активен, а все остальные аутентификаторы, включая Y, нет.

В результате получается следующая ситуация: X — активный аутентификатор. Однако переключатель Active для X не установлен. Y не активен. Но для Y выбран переключатель Active. Есть разногласия между внешним интерфейсом и фактическим статусом аутентификаторов. И это проблема.

Решением было установить псевдо-двустороннюю связь между переключателями и активным аутентификатором. Во-первых, мы поддерживаем переменную activeId в представлении, чтобы отслеживать идентификатор текущего активного аутентификатора. Затем ждем вызова для установки аутентификатора активным, чтобы вернуться и установить activeId в ID аутентификатора. Наконец, мы проходим по разделам аутентификатора. Если идентификатор раздела равен activeId, кнопка становится выбранной, иначе устанавливаем кнопку невыбранной. Вот так это выглядит:

 async _setActiveAuthenticator(authenticatorId) {
   await this._clearActiveAuthenticator();
   await this._model.setAutomaticPresenceSimulation(authenticatorId, true);
   this._activeId = authenticatorId;
   this._updateActiveButtons();
 }

_updateActiveButtons() {
   const authenticators = this._authenticatorsView.getElementsByClassName('authenticator-section');
   Array.from(authenticators).forEach(authenticator => {
     authenticator.querySelector('input.dt-radio-button').checked =
         authenticator.getAttribute('data-authenticator-id') === this._activeId;
   });
 }

async _clearActiveAuthenticator() {
   if (this._activeId) {
     await this._model.setAutomaticPresenceSimulation(this._activeId, false);
   }
   this._activeId = null;
 }

Показатели использования


Мы хотели отслеживать использование этой функции. Изначально мы придумали два варианта.

  1. Подсчитывать каждое открытие вкладки WebAuthn в DevTools. Такой подход потенциально приводит к неправильному расчету: кто-то может открыть вкладку, но не работать с ней.
  2. Отслеживать, сколько раз устанавливался флажок «Enable virtual authenticator environment» на панели инструментов. У этого варианта также была потенциальная проблема с неверным расчетом: кто-то может включать и выключать среду несколько раз за один сеанс.

В конечном итоге мы решили внедрить последнее решение, но ограничили подсчет, проверяя, была ли среда включена в сеансе один раз. Таким образом счетчик увеличивается только на 1 независимо от того, сколько раз разработчик переключал среду. Это работает, потому что новый сеанс создается каждый раз при повторном открытии вкладки, тем самым сбрасывая проверку и позволяя снова увеличивать метрику.

Резюме


Спасибо, что прочитали! Если у вас есть предложения по улучшению вкладки WebAuthn, сообщите нам об этом, заполнив баг-репорт.

На тот случай если вы задумали сменить сферу или повысить свою квалификацию — промокод HABR даст вам дополнительные 10% к скидке указанной на баннере.

image




Рекомендуемые статьи


Tags:
Hubs:
+12
Comments1

Articles

Information

Website
www.skillfactory.ru
Registered
Founded
Employees
501–1,000 employees
Location
Россия
Representative
Skillfactory School