How we created the WebAuthn tab in Chrome DevTools

Today, on the eve of the start of a new stream JavaScript course, we are sharing with you a useful translation of an article on how the WebAuthn tab was developed in Chrome DevTools, what decisions were made and why, what problem the developers faced.


Web Authentication API, also known as WebAuthn, allows servers to use public key cryptography (rather than passwords) to register and authenticate users. This API enables integration between servers and strong authenticators. These authenticators can be specialized physical devices (for example, security keys) or they can be integrated with platforms (for example, fingerprint readers). Read more about WebAuthn at

Developer problems

Prior to this project, WebAuthn did not have native debugging support in Chrome. A developer building an application that uses WebAuth required physical access to real-world authenticators. This was especially difficult for two reasons:

  1. There are many different types of authenticators. To debug a wide range of configurations and capabilities, the developer must have access to many, sometimes costly, authenticators.
  2. Physical authenticators are inherently secure, so it is usually impossible to closely examine their state.

We wanted to make things easier by adding debugging support directly to Chrome DevTools.

Solution: New Tab – WebAuthn

The WebAuthn DevTools tab greatly simplifies WebAuthn debugging by allowing developers to emulate authenticators, configure their capabilities, and check their states.


There were two parts to adding debugging support to WebAuthn.

Part 1: Add WebAuthn Domain to Chrome DevTools Protocol

First, we have implemented a new domain in Chrome DevTools Protocol (CDP)that connects to a handler that interacts with the WebAuthn backend.

CDP integrates the DevTools interface with Chromium. In our case, WebAuthn domain actions are used to connect the WebAuthn DevTools tab and the Chromium WebAuthn implementation.

The WebAuthn domain allows you to enable or disable the virtual authenticator environment, which disconnects the browser from real authenticator discovery by enabling virtual discovery.

We also provide methods on the domain that act as a thin layer to the existing virtual discovery and virtual authenticator interfaces that are part of the Chromium WebAuthn implementation. These methods include adding and removing authenticators, and creating, retrieving, and deleting their registered credentials. Take a look the code

While the WebAuthn domain made the WebAuthn tab possible, that’s not all. This domain was originally most of the WebAuthn testing API and is used by ChromeDriver to enable web platform tests. Find out more about WebAuthn API for testing

Part 2. Creating a User-Centered Tab

Secondly, we created a user-centric tab in in the DevTools interface… The tab consists of a view and a model. The domain and the tab are connected by an automatically generated agent. While three components are inevitably needed, only two of them needed to be considered: model and submission… The third component is agent, is created automatically after adding a domain. In short, an agent is a layer that transfers calls between the frontend and the CDP.


A model is a JavaScript binding agent and view layer. In our case, the model is pretty simple. It takes commands from the view, shapes the requests so that they can be used by the CDP, and then sends them through the agent. These requests are usually one-way, with no information sent back to the view. However, sometimes we return a response from the model, either to provide the ID of a newly created authenticator, or to return the credentials stored in the existing authenticator. Take a look the code


The view is used to provide the user interface that the developer will see when they open DevTools. This interface contains:

  1. Toolbar for enabling virtual authenticator.
  2. The section for adding authenticators.
  3. Section for created authenticators.

Look at the code

Toolbar for enabling the virtual authenticator environment

Most user interactions happen with one authenticator at a time, not the entire tab, so the only function we provide in the dashboard is to turn the virtual environment on and off.

Why is this needed? This is because it is important for the user to explicitly switch the environment, because toggling disables the browser from actually discovering authenticators. Therefore, while the browser is on, connected physical authenticators (such as a fingerprint reader) are not recognized.

We decided that explicit toggling gives the user the best experience, especially for those who visit the WebAuthn tab without waiting for real device detection to be disabled.

Adding an Authenticators Section

After enabling the virtual authenticator framework, we present the developer with an inline form to add a virtual authenticator. This form provides configuration options that allow the user to select the authenticator protocol and transport methods, and whether the authenticator supports resident keys and user verification. After pressing the button Add these parameters are combined and sent to the model, which makes the call to create an authenticator. As soon as the call completes, the frontend will receive a response and then change the user interface – the newly created authenticator will appear.

Authenticator view

Each time an authenticator is emulated, a section of that authenticator is added to its view. Each such section contains a name, identifier, configuration parameters, buttons for deleting or activating an authenticator, and a credential table.

Authenticator name

The name of the authenticator is variable and defaults to “Authenticator” concatenated with the last 5 characters of its ID. Originally, the name of the authenticator was its full identifier and could not be changed. We have implemented custom names so that the user can label the authenticator based on its capabilities, an emulated physical authenticator, or any alias that is easier to understand than a 36 character identifier.

Credential table

We’ve added a table to each authenticator section that shows all the credentials registered by the authenticator. Each row provides information about the credentials, as well as buttons for deleting or exporting them. We are currently collecting information to populate these tables by polling the CDP to obtain logged credentials for each authenticator. There are plans to add a credential creation event in the future.

Active button

We’ve also added a switch Active to each section of the authenticator. The active authenticator will be the only one listening and logging credentials. Without this, logging credentials for multiple authenticators would be non-deterministic, which would be a fatal error when trying to test WebAuthn with them.

Active status is implemented using the method SetUserPresence on virtual authenticators. Method SetUserPresence establishes whether user presence tests are successful for a given authenticator. If we disable it, the authenticator won’t be able to listen for credentials. Therefore, by making sure that it is enabled for no more than one authenticator (set to active), and disabling user presence for all others, it is possible to achieve complete certainty behavior. An interesting problem we faced when adding an Active button is how to avoid a race condition. Consider this situation:

  1. User presses a switch Active at the authenticator X by sending a request to the CDP to set X active. So for X the radio button is selected Activeand all other radio buttons are deselected.
  2. Immediately after that, the user clicks the switch Active authenticator Y, sending a request to CDP to set Y active. A switch is set for Y Active, and all others, including for X, are not selected.
  3. On the backend, the call to set Y as active is completed and resolved. Y is now active and all other authenticators are not.
  4. On the server side, the call to set X as active is completed and resolved. X is now active and all other authenticators, including Y, are not.

The result is the following situation: X is the active authenticator. However, the switch Active for X not set. Y not active. But for Y the radio button is selected Active… There is a disagreement between the front-end and the actual status of the authenticators. And that’s the problem.

The solution was to establish pseudo-two-way communication between the switches and the active authenticator. First, we maintain a variable activeId in the view to keep track of the id of the currently active authenticator. Then we wait for the call to set the authenticator active to go back and install activeId in the authenticator ID. Finally, we go through the authenticator sections. If the section id is activeId, the button becomes selected, otherwise we set the button unselected. This is how it looks:

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

_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;

Usage rates

We wanted to track the use of this feature. Initially, we came up with two options.

  1. Count every open WebAuthn tab in DevTools… This approach potentially leads to a miscalculation: someone can open a tab, but not work with it.
  2. Track how many times the “Enable virtual authenticator environment” checkbox was checked in the toolbar… This option also had a potential miscalculation problem: Someone could turn the environment on and off multiple times in a single session.

Ultimately we decided to implement the latter solution, but limited the count by checking if the environment was enabled in the session once… Thus, the counter is only incremented by 1 no matter how many times the developer has switched the environment. This works because a new session is created every time the tab is reopened, thus resetting the validation and allowing the metric to be increased again.


Thanks for reading! If you have any suggestions for improving the WebAuthn tab, please let us know by filling out bug report

Here are some resources in case you want to know more about WebAuthn.

In case you are planning to change the field or improve your qualifications – promotional code HABR will give you an additional 10% to the discount indicated on the banner.


More courses

Recommended articles

  • How to Become a Data Scientist Without Online Courses
  • 450 free courses from the Ivy League
  • How to learn Machine Learning 5 days a week for 9 months in a row
  • How much does a data analyst earn: an overview of salaries and vacancies in Russia and abroad in 2020
  • Machine Learning and Computer Vision in the Mining Industry

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *