Two-factor authentication (2FA) has become an essential part of securing online accounts, and Google’s Multi-Factor Authentication (MFA) is no exception. While this added security layer protects sensitive information, it can present a challenge when automating login workflows with tools like Selenium. Many QA engineers face roadblocks when dealing with MFA during test automation, as it requires a dynamic input, like a one-time password (OTP).
In this article, we’ll explore how to automate Google MFA login with Java Selenium while maintaining secure practices. Whether you're testing user flows or building scripts to streamline repetitive tasks, this guide will walk you through the process step by step. Before diving into tackling MFA with automation, let's take a look at the demo video:
Step 1: Obtain the Secret Token for Google MFA
Before automating Google MFA with Selenium, you need to manually retrieve the secret token associated with the account. The secret token is a critical piece of information used to generate one-time passwords (OTPs) for MFA. Here’s how you can obtain it:
1. Log in to Your Google Account
Open a web browser and go to Google Account Security. Sign in using your credentials if you haven’t already.
2. Navigate to the 2-Step Verification Settings
Scroll down to the "How you sign in to Google" section and click on "2-Step Verification".
You may be asked to re-authenticate by entering your password.
3. Access the Authenticator App Setup
Under the "Authenticator" section, click on "Add Authenticator App" (or "Change Authenticator App" if it’s already configured). Select your device type (Android or iPhone) and click "Next".
4. Retrieve the Secret Token
On the screen displaying a QR code for the authenticator app, look for a link labeled "Can’t scan it?". Click this option to reveal the secret key (a string of alphanumeric characters).
5. Secure the Secret Token
Copy the secret token and store it securely, as it will be used in your test automation.
Use a password manager or a secure storage tool to keep it safe. Never expose the secret key in your automation scripts or version control systems (e.g., GitHub). Consider using environment variables or secret management tools to handle it securely.
6. Verify the Token
Test the secret key by setting it up in an authenticator app (e.g., Google Authenticator, Authy, or another compatible app). Ensure the OTP generated matches the code required for logging in.
With the secret token in hand, you’re ready to move on to the next step.
Step 2: Automate OTP Generation Using Steven's TOTP Library
With the secret token obtained in Step 1, the next step is to generate the one-time passwords (OTPs) programmatically. To accomplish this, I’ve leveraged Steven Sam's TOTP library (dev.samstevens.totp) for its simplicity and robust implementation of time-based OTP (TOTP) algorithms.
Below is my implementation of a utility class, TotpHelper, that dynamically calculates the OTP based on the shared secret and the current time:
Implementation of TotpHelper:
package com.google.helpers;
import java.util.concurrent.TimeUnit;
import dev.samstevens.totp.code.DefaultCodeGenerator;
import dev.samstevens.totp.code.HashingAlgorithm;
import dev.samstevens.totp.exceptions.CodeGenerationException;
public class TotpHelper {
public static String generateTOTP(String secret) {
// Define the time step (30 seconds per interval)
var timeStep = TimeUnit.SECONDS.toMillis(30);
// Get the current system time and calculate the counter
var currentTimeMillis = System.currentTimeMillis();
var counter = currentTimeMillis / timeStep;
// Initialize the TOTP generator with SHA1 algorithm and 6-digit OTPs
var codeGenerator = new DefaultCodeGenerator(HashingAlgorithm.SHA1, 6);
try {
// Generate the OTP using the shared secret and counter
var otp = codeGenerator.generate(secret, counter);
return otp;
} catch (CodeGenerationException e) {
e.printStackTrace();
return null;
}
}
}
Key Features of the Implementation:
📌TOTP Standard: The library implements the TOTP algorithm based on RFC 6238, ensuring compatibility with major authenticator apps like Google Authenticator.
📌Time-Based Counter: The counter is derived from the current system time, divided into 30-second intervals (timeStep = 30,000 ms). This ensures that the OTP changes every 30 seconds.
📌Hashing Algorithm: The DefaultCodeGenerator uses the SHA1 hashing algorithm with a 6-digit code length, which is the standard for most TOTP implementations.
📌Error Handling: The generateTOTP method handles potential exceptions (CodeGenerationException) to ensure the automation flow isn’t disrupted by unexpected errors.
Step 3: Implement the Selenium Test for Google Login Automation
Now that you've set up the secret token generation, the next step is to implement the actual login test using Selenium. This test will automate logging into a Google account using the username, password, and the TOTP (Time-Based One-Time Password) for MFA (Multi-Factor Authentication) by leveraging the TotpHelper class I created earlier.
package com.google.tests;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.annotations.Test;
import com.google.helpers.TotpHelper;
import com.google.pages.LoginPage;
import io.github.bonigarcia.wdm.WebDriverManager;
public class LoginTest {
@Test
void test_Login() {
var username = "xxx";
var password = "xxx";
var secret = "xxx";
// Setup WebDriver
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-blink-features=AutomationControlled"); // Disable automation detection
var driver = new ChromeDriver(options);
// Interact with the login page
var loginPage = new LoginPage(driver);
loginPage.loadPage();
loginPage.inputUsername(username);
loginPage.clickNextButton();
loginPage.inputPassword(password);
loginPage.clickNextButton();
loginPage.clickGoogleAuthenticatorOption();
// Generate and input OTP
var otp = TotpHelper.generateTOTP(secret);
loginPage.inputOtp(otp);
loginPage.clickNextButton();
// Wait for successful login
loginPage.waitToBeLoadedSuccessfully();
// Close the browser
driver.quit();
}
}
Thank you for reading my article. Have a great day!