mkhbragg
mkhbragg

Reputation: 131

Selenium Tests: Authenticate with Webauthn

In my use case, there is a registration page that triggers the browser-specific webauthn flow. For example in Chrome on a Mac you will see this series of popups:

  1. Pick an option between USB security key and Built-in sensor
  2. MacOS confirmation with Touch ID
  3. Confirmation dialog from Chrome requesting access to your security key

Besides https://w3c.github.io/webauthn/#add-virtual-authenticator I haven't found much documentation about authenticating with webauthn as part of a selenium test. What resources are available to help devs figure out how to test webauthn with Selenium in JavaScript? I have also checked out https://github.com/SeleniumHQ/selenium/issues/7829 but the example test case does not make sense to me. Examples would be hugely appreciated.

Update with solution for js:

  import { Command } from 'selenium-webdriver/lib/command';

  addVirtualAuthenticator = async () => {
    await this.driver.getSession().then(async session => {
      this.driver
        .getExecutor()
        .defineCommand('AddVirtualAuthenticator', 'POST', `/session/${session.id_}/webauthn/authenticator`);

      let addVirtualAuthCommand = new Command('AddVirtualAuthenticator');
      addVirtualAuthCommand.setParameter('protocol', 'ctap2');
      addVirtualAuthCommand.setParameter('transport', 'internal');
      addVirtualAuthCommand.setParameter('hasResidentKey', true);
      addVirtualAuthCommand.setParameter('isUserConsenting', true);
      await this.driver.getExecutor().execute(addVirtualAuthCommand);
    });
  };

Note that this.driver is of type WebDriver.

Call addVirtualAuthenticator before hitting any code that interacts with navigator (in our case user registration involved a call to navigator.credentials.create). If you need access to the publicKey, i.e. via navigator.credentials.get({ publicKey: options }) during login, then hasResidentKey is critical.

Upvotes: 3

Views: 5668

Answers (2)

Justin Lambert
Justin Lambert

Reputation: 978

This is worst practice in selenium

Two Factor Authentication shortly know as 2FA is a authorization mechanism where One Time Password(OTP) is generated using “Authenticator” mobile apps such as “Google Authenticator”, “Microsoft Authenticator” etc., or by SMS, e-mail to authenticate. Automating this seamlessly and consistently is a big challenge in Selenium. There are some ways to automate this process. But that will be another layer on top of our Selenium tests and not secured as well. So, you can avoid automating 2FA.

There are few options to get around 2FA checks:

1.Disable 2FA for certain Users in the test environment, so that you can use those user credentials in the automation. 2.Disable 2FA in your test environment. 3.Disable 2FA if you login from certain IPs. That way we can configure our test machine IPs to avoid this.

Upvotes: -2

Nina Satragno
Nina Satragno

Reputation: 601

A good resource for an example if you're implementing this in java and using selenium 4 is the tests on selenium itself. You basically need to

  • Create a virtual authenticator

    In your case, you should set the transport to internal and hasUserVerification to true to simulate touchID.

VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
options.setTransport(Transport.INTERNAL)
       .hasUserVerification(true)
       .isUserVerified(true);
VirtualAuthenticator authenticator =
    ((HasVirtualAuthenticator) driver).addVirtualAuthenticator(options);
  • Perform the action that triggers registration.

    If everything goes right, the browser should not show a dialog. Instead, it should immediately return a credential.

For any other language or selenium version, you will need to drop into calling the WebDriver protocol directly. As you pointed out, the W3C spec has documentation on the protocol endpoints.

For java, it might be something like

browser.driver.getExecutor().defineCommand(
    "AddVirtualAuthenticator", "POST", "/session/:sessionId/webauthn/authenticator");

// ...

Command addVirtualAuthCommand = new Command("AddVirtualAuthenticator");
addVirtualAuthCommand.setParameter("protocol", "ctap2");
addVirtualAuthCommand.setParameter("transport", "usb");
browser.driver.getExecutor().execute(addVirtualAuthCommand);

For javascript, you should be able to use defineCommand and webDriver.execute in a similar fashion.

Upvotes: 5

Related Questions