MailSlurp logo

How to test 2FA OTP login using SMS codes with Playwright

The ultimate guide to testing OAuth one-time-password flows with real SMS MFA. Authentication tests with programmable TXT message APIs.

By Jack MahoneyUpdated

Use with your tools
Playwright, Cypress, Selenium, and k6 logos for automated message tests

Do you develop or test applications? Then you may want to know about OTP and MFA testing. This is a technique whereby use sign-up and account confirmation flows can be tested end-to-end using disposable email account and phone number APIs.

What is OTP and why would I use it?

One time passwords (OTP) with MFA are a popular mechanism for securing web applications. Many applications today rely on multi-factor authentication (2FA/MFA) to login and signup users as it is considered more secure than a basic username/password flow.

An example SMS verification form on our demonstration application

MFA means that a user must provide more than a simple password to access an application - they must also verify their identity by another means.

Usually secondary authentication is performed via an SMS one time password that is sent to a user's mobile phone. This code is unique and expires (hence the name OTP or one time password). The user must receive the code and enter it to a confirmation window during sign up. This proves that the user is who they say they are.

Testing MFA and OTP

Testing auth/OAuth using real phone numbers is an excellent way to automate your integration/end-to-end test suite and ensure that your application is properly handling user authentication and security. Using a phone number automation service such as MailSlurp we write a test as follows:

  • create a test phone number
  • use Playwright browser automation to load our application
  • sign up for the app using the test number
  • capture the inbound SMS code in tests and submit it to the app
  • confirm we gain access to the application

In this guide we will show you this exact test using a dummy OAuth SMS app located at playground-sms.mailslurp.com.

Playwright setup

We will use Microsoft's great Playwright tool (available in C#, Java, Typescript, NodeJs, and Python) to write an automated browser test that loads our application in chrome and performs checks. We will use Typescript for this example. (Find the code on Github.)

MailSlurp video Open video on YouTube

Create a test project

First create a new project on your machine with NodeJS.

mkdir test
cd test
npm init -y

Install playwright

Setup playwright by installing and configuring it.

npm init playwright@latest

You'll see something like so in your directory:

playwright.config.ts
package.json
package-lock.json
tests/
  example.spec.ts
tests-examples/
  demo-todo-app.spec.ts

Running tests

Execute tests using the npx playwright test command and view the results in your terminal or with the npx playwright show-report command. Now let's write some tests for MFA login.

Testing user sign up

To test authentication end-to-end using MFA and SMS we need to use Playwright to load our application, fill out a sign-up form, then capture an SMS code and submit it. Let's do that.

Load the create account page

To begin testing use the page object in your test to load the playground login screen:

// load playground app
await page.goto("https://playground-sms.mailslurp.com");
await page.click('[data-test="sign-in-create-account-link"]');

Use a test phone number

We have created a US phone number in the MailSlurp dashboard and we can fetch it in our tests using the MailSlurp Javascript client.

// fetch a phone number in US from our account
const mailslurp = new MailSlurp({ apiKey })
const { content }= await mailslurp.phoneController.getPhoneNumbers({
  phoneCountry: GetPhoneNumbersPhoneCountryEnum.US
})
const phone = content?.[0]!!

To create phone numbers please follow the phone number guide.

Fill sign-up form

Next we can use the phone number to fill out the username and password form:

const password = "test-password-123"
// fill sign up form
await page.fill('input[name=phone_line_number]', phone.phoneNumber.replace("+1", ""));
await page.fill('input[name=password]', password);

The form will be filled in Playwright like so:

Wait for and capture SMS

Now we can use MailSlurp to wait for an expected SMS to arrive at our phone number and extract the verification code:

import { MailSlurp } from 'mailslurp-client';

export async function checkSmsLogin(
  mailslurp: MailSlurp,
  phoneNumber: { id: string; phoneNumber: string },
  actions: {
    requestCode: (number: string) => Promise<void>;
    submitCode: (code: string) => Promise<void>;
    assertSignedIn: () => Promise<void>;
  },
) {
  // Reserve this number exclusively for this attempt before calling the helper.
  const since = new Date();
  await actions.requestCode(phoneNumber.phoneNumber);
  const sms = await mailslurp.waitController.waitForLatestSms({
    waitForSingleSmsOptions: {
      phoneNumberId: phoneNumber.id,
      since,
      timeout: 60_000,
      unreadOnly: false,
    },
  });
  // Adapt this exact template to the SMS your application sends.
  const match = /^Your code: ([0-9]{6})$/.exec(sms.body ?? '');
  if (!match) throw new Error('SMS did not match the expected OTP template');
  await actions.submitCode(match[1]);
  await actions.assertSignedIn();
}

Confirm user SMS OTP

Use the code we extracted to fill the username field:

// enter confirmation code
await page.fill('[data-test="confirm-sign-up-confirmation-code-input"]', code);

Confirm the user by submitting the OTP passcode

Then submit the form:

await page.click('[data-test="confirm-sign-up-confirm-button"]');

Sign in and view user data

Now sign into the app:

// fill out username (email) and password
await page.fill('[data-test="username-input"]', phone.phoneNumber);
await page.fill('[data-test="sign-in-password-input"]', password);

Submit the login form and expect a greeting:

// submit
await page.click('[data-test="sign-in-sign-in-button"]');
await page.waitForSelector("[data-test='greetings-nav']")

If successful we will see the greeting screen (a happy dog):

Conclusion

MFA login using two-factor authentication is an important technology for modern web apps. Testing OAuth SMS flows using SMS services like MailSlurp is a great way to verify user authentication in your application. See the developer docs for more information.