Sponsored Link
Looking for a unified way to  viewmonitor, and  debug Playwright Test Automation runs?
Try the Playwright Dashboard by today. Use the coupon code PWSOL10 for a 12 month 10% discount.

Playwright Login Test With Two Factor Authentication (2FA) Enabled (TOTP)

This week I'll walk through a Playwright Test with 2FA enabled. 2FA or Two Factor Authentication is a common recommended security measure to protect users accounts across the internet. Having a way to test this functionality in your CI/CD pipelines within your Playwright tests can ensure that this functionality doesn't get broken as new features and bug fixes are made to the codebase.

Example of 2FA with TOTP

A common way to implement 2FA is by using TOTP, this stands for Time-based One-time Password. Typically the TOTP service provides you with a QR code or a Key that you input into an Authenticator App, after you initially create your user account. There is an example of a file below.

The key above A7VDILVI5ZTJPPGR, is used to generate the time based password. If you want to view the current 6 digit code that is generated at this current moment you can use a tool like - https://it-tools.tech/otp-generator, paste in the OTP key and see the code that is generated every 30 seconds. You can think of this step the same as scanning the barcode into your Authenticator App, the key is what gets saved to your device and is used to generate the random rolling 6 digit password.

Install otpauth dependency

In the same way the above web app can take the key and generate the 6 digit TOTP code, we will be using the Node package otpauth, within our Playwright tests. Let's first install this into our project.

npm install otpauth

Build logic to generate the one time password

Once installed we'll go ahead and create a new helper file in the lib folder named otp.ts.

// lib/helpers/otp.ts

import * as OTPAuth from "otpauth";

export function generateOTP(secret: string) {
  const totp = new OTPAuth.TOTP({
    secret: secret,
    digits: 6,
    algorithm: "sha1",
    period: 30,
  });

  return totp.generate();
}

This code will generate the One Time Password that will be used to validate and login with the key that was provided above. All we have to do from a test now is call generateOTP(secretKey) while passing in the secretKey we created from the last step.

One thing to note on this code, if the digits, algorithm, or period are different for your system under test you will need to adjust these.

Implement within a Playwright test

For our example we will be using https://practicesoftwaretesting.com website. This is a tool built by Roy de Kleijn, which can be used as a test site for exploring or writing test automation.

All the code I'm showing off can be found in this pull request from the repo linked below.

GitHub - playwrightsolutions/playwright-practicesoftwaretesting.com: Example using Playwright against site https://practicesoftwaretesting.com
Example using Playwright against site https://practicesoftwaretesting.com - playwrightsolutions/playwright-practicesoftwaretesting.com

If you reviewed the pull request closely you'll notice I added a few helpers and data factories in order to setup test data, as the practicesoftwaretesting.com site database resets multiple times a day. For our purposes I'm not going to focus on test data setup/creation/management but rather how to implement 2fa into your tests.

Creating the data

First off you should register a new account. Once registered and logged back in, you can visit the profile page and you will see the section above with a QR code and a key. You can scan this into an authenticator app or use this tool I linked earlier to generate the 6 digit 1 time password from the key. Once you have a 6 digit code, enter and verify the TOTP code.

Update .env file

Next we'll update the .env file with the username, password, and one time password key.

//.env 

PLAYWRIGHT_USERNAME=playwrightsolutions@practicesoftwaretesting.com
PLAYWRIGHT_PASSWORD=testyMctesterface!@5
OTP_KEY=LMWOOBI4DSVGGR4W

2FA login spec

At this point the spec is pretty straight forward. notice we set the otpKey from the environment variable along with the email and passwords. We use these to login on the main page. The next section includes the generateOTP() method where we pass in the otpKey, to generate the 6 digit code we will use to login.

// tests/auth/login.2fa.spec.ts

import { generateOTP } from "@helpers/otp";
import { LoginPage } from "@pages";
import { test, expect } from "@playwright/test";

test("Login with 2FA enabled", async ({ page }) => {
  const playwrightEmail = process.env.PLAYWRIGHT_USERNAME;
  const playwrightPassword = process.env.PLAYWRIGHT_PASSWORD;
  const otpKey = process.env.OTP_KEY;

  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login(playwrightEmail, playwrightPassword);

  const otpCode = generateOTP(otpKey);

  await loginPage.totp.fill(otpCode);
  await loginPage.verifyTotp.click();
  expect(await loginPage.navMenu.innerText()).toContain("Testy McTester");
});

The full end to end flow looks like this.

0:00
/0:12

Final Thoughts

The example we had was quite straight forward, you may run into a scenario where you may not have the key but you do have a QR code to use. This guide will walk you through how to extract the key from a QR code utilizing a mobile device.


Thanks for reading! If you found this helpful, reach out and let me know on LinkedIn or consider buying me a cup of coffee. If you want more content delivered to you in your inbox subscribe below, and be sure to leave a ❤️ to show some love.