Cypress - Automating SSO Login with Multi-Factor Authentication using Mobile Authenticator App OTPs in Azure AD
Introduction In our ever-evolving software projects, one of the most significant updates we encountered was the shift to Single Sign-On (SSO) logins across all the environments. While SSO enhances security and user experience, it posed a challenge for our automated tests. Our test cases, originally designed for username and password-based logins, were suddenly rendered obsolete. The new login process now relied on Azure Active Directory (Azure AD) and introduced Multi-Factor Authentication (MFA), requiring OTPs from a mobile authenticator app. This article explores how we tackled this challenge and automated the Azure AD login process with Cypress, a popular end-to-end testing framework. The Challenge The main hurdle was automating a login flow that incorporated Multi-Factor Authentication, which relied on OTPs generated by a mobile authenticator app. Our objective was to eliminate the manual step of entering OTPs during testing. The Solution To automate this complex login process, we established a comprehensive setup that involved the following prerequisites: 1. Obtain the Secret Key To facilitate the OTP generation, we needed a secret key from your Microsoft Account Security info section. Here's how you can obtain it: Log in to https://login.microsoftonline.com/ with your SSO credentials. After logging in, click on your Profile icon and select "View Account." On the account info page, choose "Security info." Since MFA is enabled, you'll see options like SMS and Authenticator app-based OTP methods. Click on "+ Add sign-in method." Select "Authenticator app" from the dropdown menu and click "Add." A dialog box will prompt you to install the Microsoft Authenticator app on your mobile device (you can use other authenticator apps like Google Authenticator). Depending on your app, click "Next" or choose the option "I want to use a different authenticator app." In the subsequent dialog, click "Next" to set up your account. A dialog with a QR code will appear. Look for the button below the QR code that says "Can't scan image?" Click on "Can't scan image?" to reveal your account name along with your email address and the secret key. Copy and save this information for later use. Use your mobile app to scan the QR code. It will set up your account, and new codes will start appearing in your mobile app. In the browser, you'll be prompted to enter the 6-digit code from the newly added account in your Authenticator app. Enter the code and click "Next." Your new Authenticator app method setup is complete. Ensure you safeguard the secret key obtained in the previous step. 2. Add Dependency to Your Cypress Project In your Cypress project repository, add the following dependency: npm install -D otplib Additionally you need to add below configuration to your 'cypress.config.js' experimentalModifyObstructiveThirdPartyCode: true 3. Implement the Login Flow You can either write your custom login flow for the Microsoft login pages or use the provided code as a reference. The code demonstrates how to utilize otplib methods to generate OTPs at runtime using the secret key saved in a Cypress environment variable file. Please note that the provided code is tailored to my application flow and may require adjustments to suit your specific application. import { authenticator } from 'otplib'; const validateLocalStorage = localStorage => Cypress._.some(localStorage, (value, key) => key.includes('CognitoIdentityServiceProvider'), ) Cypress.Commands.add('getTOTP', () => { const otp = authenticator.generate(Cypress.env('azureSecret')) return otp }) Cypress.Commands.add('sessionLogin', () => { cy.session('login', () => cy.loginViaAzureAD(), { validate: () => cy.getAllLocalStorage().should(validateLocalStorage), cacheAcrossSpecs: true }) return cy.visit('/').then(() => { cy.closeCookieBanner() cy.extractToken() }) }) Cypress.Commands.add('loginViaAzureAD', () => { cy.origin('https://login.microsoftonline.com/', () => { cy.visit('/') cy.get("[id='i0116']").type(Cypress.env('username')); cy.get("[id='idSIButton9']").click() cy.wait(2000) }) cy.origin('', () => { cy.get("[id='passwordInput']").type(Cypress.env('password')); cy.get("[id='submitButton']").click(); cy.wait(5000) }) cy.url().should("contain", "DeviceAuthTls").then(() => { cy.visit('/') cy.getTOTP().then((otp1) => { const objOTP = { otp: otp1 } cy.origin('https://login.microsoftonline.com/', { args: objOTP }, ({ otp }) => { cy.wait(1000) cy.get("[id='idTxtBx_SAOTCC_OTC']").type(otp); cy.get("[id='idSubmit_SAOTCC_Continue']").click(); }) }) cy.wait(5000) cy.url().should("contain", "/DAEWebApp/#/landing") cy.closeCookieBanner() }) })

Introduction
In our ever-evolving software projects, one of the most significant updates we encountered was the shift to Single Sign-On (SSO) logins across all the environments. While SSO enhances security and user experience, it posed a challenge for our automated tests. Our test cases, originally designed for username and password-based logins, were suddenly rendered obsolete. The new login process now relied on Azure Active Directory (Azure AD) and introduced Multi-Factor Authentication (MFA), requiring OTPs from a mobile authenticator app.
This article explores how we tackled this challenge and automated the Azure AD login process with Cypress, a popular end-to-end testing framework.
The Challenge
The main hurdle was automating a login flow that incorporated Multi-Factor Authentication, which relied on OTPs generated by a mobile authenticator app. Our objective was to eliminate the manual step of entering OTPs during testing.
The Solution
To automate this complex login process, we established a comprehensive setup that involved the following prerequisites:
1. Obtain the Secret Key
To facilitate the OTP generation, we needed a secret key from your Microsoft Account Security info section. Here's how you can obtain it:
- Log in to https://login.microsoftonline.com/ with your SSO credentials.
- After logging in, click on your Profile icon and select "View Account."
- On the account info page, choose "Security info." Since MFA is enabled, you'll see options like SMS and Authenticator app-based OTP methods.
- Click on "+ Add sign-in method."
- Select "Authenticator app" from the dropdown menu and click "Add."
- A dialog box will prompt you to install the Microsoft Authenticator app on your mobile device (you can use other authenticator apps like Google Authenticator).
- Depending on your app, click "Next" or choose the option "I want to use a different authenticator app."
- In the subsequent dialog, click "Next" to set up your account.
- A dialog with a QR code will appear. Look for the button below the QR code that says "Can't scan image?"
- Click on "Can't scan image?" to reveal your account name along with your email address and the secret key. Copy and save this information for later use.
- Use your mobile app to scan the QR code. It will set up your account, and new codes will start appearing in your mobile app.
- In the browser, you'll be prompted to enter the 6-digit code from the newly added account in your Authenticator app. Enter the code and click "Next."
- Your new Authenticator app method setup is complete. Ensure you safeguard the secret key obtained in the previous step.
2. Add Dependency to Your Cypress Project
In your Cypress project repository, add the following dependency:
npm install -D otplib
Additionally you need to add below configuration to your 'cypress.config.js'
experimentalModifyObstructiveThirdPartyCode: true
3. Implement the Login Flow
You can either write your custom login flow for the Microsoft login pages or use the provided code as a reference. The code demonstrates how to utilize otplib methods to generate OTPs at runtime using the secret key saved in a Cypress environment variable file. Please note that the provided code is tailored to my application flow and may require adjustments to suit your specific application.
import { authenticator } from 'otplib';
const validateLocalStorage = localStorage =>
Cypress._.some(localStorage, (value, key) =>
key.includes('CognitoIdentityServiceProvider'),
)
Cypress.Commands.add('getTOTP', () => {
const otp = authenticator.generate(Cypress.env('azureSecret'))
return otp
})
Cypress.Commands.add('sessionLogin', () => {
cy.session('login', () => cy.loginViaAzureAD(), {
validate: () => cy.getAllLocalStorage().should(validateLocalStorage),
cacheAcrossSpecs: true
})
return cy.visit('/').then(() => {
cy.closeCookieBanner()
cy.extractToken()
})
})
Cypress.Commands.add('loginViaAzureAD', () => {
cy.origin('https://login.microsoftonline.com/', () => {
cy.visit('/')
cy.get("[id='i0116']").type(Cypress.env('username'));
cy.get("[id='idSIButton9']").click()
cy.wait(2000)
})
cy.origin('', () => {
cy.get("[id='passwordInput']").type(Cypress.env('password'));
cy.get("[id='submitButton']").click();
cy.wait(5000)
})
cy.url().should("contain", "DeviceAuthTls").then(() => {
cy.visit('/')
cy.getTOTP().then((otp1) => {
const objOTP = { otp: otp1 }
cy.origin('https://login.microsoftonline.com/', { args: objOTP }, ({ otp }) => {
cy.wait(1000)
cy.get("[id='idTxtBx_SAOTCC_OTC']").type(otp);
cy.get("[id='idSubmit_SAOTCC_Continue']").click();
})
})
cy.wait(5000)
cy.url().should("contain", "/DAEWebApp/#/landing")
cy.closeCookieBanner()
})
})
Conclusion
By following these steps, we successfully automated the Azure AD login process with Multi-Factor Authentication, making our test automation more efficient and eliminating the need for manual OTP entry. This approach not only streamlines testing but also ensures the reliability and security of our SSO-enabled applications.
Automation, especially in the realm of authentication, can be challenging, but with the right tools and strategies, we can overcome these obstacles and deliver more robust software. In the fast-paced world of software development, adaptability and innovation are key, and automating complex processes like Azure AD login with MFA is a significant step forward.