How to Do MFA Login With Playwright

Published by Mika Berglund on

playwright mfa login

Last year, in 2023, I started looking more into Playwright for both testing and browser automation. I also ended up in a situation where I needed to do login to Microsoft Entra ID with an account with MFA enabled. This article describes how to write your Playwright code to successfully perform MFA login to Microsoft Entra ID.

What Is Playwright?

Before we dive in, a few words about Playwright if you are not that familiar with it yet. Playwright is a cross-platform library for automating web browsers. It allows you to write end-to-end tests, capture screenshots, generate PDFs, and perform web scraping using a consistent and user-friendly API. Playwright supports Chromium, Firefox, and WebKit browsers, and can run on Windows, Linux, and macOS.

You might have heard of Selenium. Playwright is kind of the new kid on the block in browser automation. I used to do a lot of testing and browser automation with Selenium back in the day. But Playwright has really become my favourite browser automation library. I feel the code with Playwright is much simpler and lighter than with Selenium. For instance, I don’t have to add random delays or other checks to make sure that an element is visible or clickable before I start using it in my code. Playwright does all of that for me. With Selenium, this is not the case. Your Selenium tests may still fail randomly because your timing is not right. Or then you just add unnecessary long delays or something like that. That’s all history with Playwright.

Disclaimer! It’s been several years since I’ve last worked with Selenium, so the issues I described above may be all history.

What’s the Problem With Playwright and MFA Login?

If you would have just a username and password, it would be quite simple to create automation with Playwright that would take care of login. But with MFA, login is a bit trickier. How do you get a TOTP (Time-based One-Time Password) from your authenticator app to your Playwright automations?

Fortunately, it is quite simple at the end of the day. The main points of the solution are:

  • Configure MFA for the account so that it can be leveraged in code
  • Add code to your automations that use the configuration to produce TOTPs as needed

This article walks you through all the steps you need to master MFA login in your Playwright automations.

Configure User Account

To create and configure a user account that has MFA enabled and can be used with Playwright automations, follow my Create a Shared Account With MFA in Microsoft Entra ID article that I wrote previously.

Code for Playwright Supporting MFA Login

In this chapter I’ll go through each step that is required to get your MFA login working in Playwright. You find the code examples in the chapters below in a Github repository I set up. This repository contains a fully working example of how to log in to the Microsoft 365 portal with a user account that has MFA enabled. The code checks whether MFA is required during login, and adapts accordingly. The sub chapters are associated with named blocks in the sample code.

Referenced Libraries

The application references the following Nuget packages.

  • Microsoft.Playwright – The library that contains everything you need to run browser automations with Playwright.
  • Otp.NET – A library that provides TOTP computing capabilities.

Initialize Objects

All Playwright automations start by creating a Playwright object using Playwright.CreateAsync(). You will also need a browser type and browser object. In the sample code I create the browser object directly by using the pw.Chromium.LaunchAsync() method. However, if you would like to run your automations on different browsers, you would create a separate browser type object, and then send that away to the implementation that does the actual automation. Playwright supports 3 browser types – Chromium, Firefox and Webkit.

With the browser object, you create a browser context that represents the session you are running. Using the browser context, you create a new page object that you then use in your automations.

Navigate to Application

In order to have a web application to interact with, you first navigate to the page. In the sample application we use the URL https://www.microsoft365.com/login which would redirect directly to the login page. Another option would be to navigate to https://www.microsoft365.com/ and click the Sign in button.

Define Common Selectors

There are several CSS selectors that we use across the application. These are defined at the start of the application, so that we can reuse them across the rest of the code.

Provide Username and Password

Now this is where the magic starts to happen. The first thing we do is to fill in the username in the username text box. This is done with the FillAsync() method of the page object. Then we click the Next button by using the ClickAsync() method.

Note! Playwright takes care of waiting for an element to be available for interaction. This means that Playwright will not perform the interaction before the element is visible, clickable, or otherwise available for interaction. This makes your code so much clearer, when you don’t have to add random delays between every line of code, just to be on the safe side. The default timeout of 30 seconds is more than enough. If an element takes longer than the assigned timeout to become available, Playwright will throw an exception.

After clicking the Next button, the code waits for the username text box to be detached from the DOM. This needs perhaps a bit of explanation. As you know, the login to Microsoft cloud services such as Microsoft 365 is built up of multiple views. The transitions between these views use a lot of animation. In addition, the different views reuse the same CSS selectors on elements that have different meanings on different views. For instance, the Next button on the username view matches the same CSS selector as the Yes button on the Keep me signed in view.

So, we wait for the username text box to disappear before we start processing the password.

Note that this is not the same as waiting for a fixed amount of seconds. This is waiting until something has occurred, which is completely fine.

Next step is to handle the password. This follows the same logic as handling the username.

  • Fill in the password
  • Click on the Next button
  • Wait for the password text box to disappear from the DOM

Handle MFA Login

Since MFA is not required for every login, we need to determine whether MFA is required for the current login that we are processing. We do this by waiting for one of two elements.

  1. TOTP text box
  2. Keep me signed in (KMSI) checkbox

Whichever element is found first determines how we proceed in the code. If the KMSI checkbox was found first, then we know that MFA was not required and we complete the login process by clicking the Yes button one more time.

On the other hand, if the TOTP text box was found, then we know that we need to handle MFA login. This is where we use the MFA secret key that you preferably have stored in your password manager. Use the Otp.NET library produce a TOTP. Then, fill the TOTP text box and click the Next button.

Again, after clicking the Next button, we wait for the TOTP text box to disappear from the DOM before continuing to the next step.

Keep Me Signed In

This is the last step in the login process. Here, it actually does not make any sense to check the keep me signed in checkbox, because each browser context you start in Playwright is a clean session. It’s just like you would start your browser in the In Private/Incognito mode every time you fire up a browser with Playwright.

You can populate a browser context with storage state from a previous session including login information, but I’m skipping that here for simplicity.

So, we just click on the Yes button to complete the login.

Conclusion

So, with these steps it should be possible for you to test any application that you log in to using a Microsoft Entra ID account that has MFA enabled. The same principles apply also to accounts in Microsoft Entra External ID or Azure AD B2C. I would assume that the same works with any identity provider that supports TOTP.


0 Comments

Leave a Reply

Avatar placeholder

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