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.

Using Playwright automation to test user sign up in a real application. We can login with a phone number we control in code and capture the SMS sent verification code.

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.

Load
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 (avaible 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.)

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

::image{src="/examples-screenshots/playwright-sms/02-create-account.jpg" srcWebp="/examples-screenshots/playwright-sms/02-create-account.webp" title="Create account page" className="prose-img-inset" caption="Our dummy app asks for a phone number and password during sign up."}

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]!!

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:

Fill form
Playwright will use our automated phone number to create a new account.

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:

await page.click('[data-test="sign-up-create-account-button"]');
// wait for verification code
const sms = await mailslurp.waitController.waitForLatestSms({
  waitForSingleSmsOptions: {
    phoneNumberId: phone.id,
    unreadOnly: true,
    timeout: 30_000,
  }
})
// extract the confirmation code (so we can confirm the user)
const code = /([0-9]{6})$/.exec(sms.body)?.[1]!!;

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
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);
Sign in
Sign in after confirmation

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):

greeting

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.