Setting up Selenium/Java to test Multi-Factor Authentication (MFA)
This post describes how to set up Selenium and run a test case to authenticate with multi-factor authentication (MFA).
By following these instructions, you can use Selenium and Java to authenticate to Login.gov and enter a one-time code using your Java code to simulate an authenticator app.
1. Install Java
JDK is required as the Selenium code I will be writing is written in Java.
a. Navigate to https://www.oracle.com/java/technologies/downloads/archive/.
b. Download Java. For compatibility reasons with my IDE, I installed jdk-11.0.21_windows-x64_bin.exe from https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html.
c. After installation, make sure that you are able to invoke Java from the command line (e.g., use "java -version" as an example). If not, you may have to add it to your system path manually.
2. Install Eclipse
Eclipse is the development tool of choice (aka IDE). All Java code will be written in Eclipse.
a. Navigate to https://www.eclipse.org/downloads/.
b. Click on the Download Packages link.
c. Download "Eclipse IDE for Java Developers" and install it.
3. Download Apache Maven
Maven is used to manage package dependencies. By using Maven, you do not need to download Selenium, Selenium webdrivers, or the security JARs. Maven will download, import, and reference all these dependencies for you.
a. Navigate to https://maven.apache.org/download.cgi.
b. Download the binary zip archive (e.g., apache-maven-3.9.6-bin.zip) and extract it to a set folder (e.g., C:\software\apache-maven-3.9.6).
c. Add MVN_HOME as a system variable and append %MVN_HOME%\bin to the user variable Path.
d. Make sure that you are able to invoke Maven from the command line (e.g., use "mvn -v" as an example).
5. Create a Maven project in Eclipse
a. In Eclipse, click on File > New > Maven Project.
b. Select "Create a simple project (skip archetype selection)" then click Next.
c. Add a group id (e.g., MFA) and artifact id (e.g., SeleniumTest) then click Finish.
d. Add the following dependencies to the pom.xml file, which should look like this:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>MFA</groupId>
<artifactId>SeleniumTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.16.1</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.6.2</version>
</dependency>
<dependency>
<groupId>de.taimos</groupId>
<artifactId>totp</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
</project>
e. Right-click on src/test/java and select New > Class.
f. For Package, enter "mfa".
g. For Name, enter "TestAuthentication".
h. Check public static void main(String[] args), then click Finish.
i. Add this code. Note that a portion of the code in the main() method is commented out (for now).
package mfa;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
import de.taimos.totp.TOTP;
public class TestAuthentication {
public static void main(String[] args) {
// This secretKey is provided by the application as a one-time setup
// Do not use this key in your authenticator app, but put it here in the Java code instead
String secretKey = "PYYOO2RHNGSPMYXR3XXQTIACXDDSK4AT";
String lastCode = null;
String code = null;
code = getTOTPCode(secretKey);
if (!code.equals(lastCode)) {
System.out.println(code);
}
lastCode = code;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
/* Keep this commented until authenticator app is registered on Login.app
WebDriverManager.chromedriver().setup();
ChromeDriver driver = new ChromeDriver();
driver.get("https://secure.login.gov");
driver.findElement(By.xpath("//*[@id=\"user_email\"]")).sendKeys("user@someemail.com");
driver.findElement(By.xpath("//*[contains(@id, 'password-toggle-input-')]")).sendKeys("welcome1");
driver.findElement(By.xpath("//*[@id=\"new_user\"]/lg-submit-button/button")).click();
driver.findElement(By.xpath("//*[contains(@id, 'code-')]")).sendKeys(code);
driver.findElement(By.xpath("//*[@id=\"main-content\"]/div/form/lg-submit-button/button")).click();
// driver.close();
*/
}
// Generate the Google Authenticator 20 bytes secret key encoded as base32
public static String generateSecretKey() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
Base32 base32 = new Base32();
return base32.encodeToString(bytes);
}
// Convert base32 encoded secret keys to hex and use the TOTP to turn them into 6-digits codes based on the current time
public static String getTOTPCode(String secretKey) {
Base32 base32 = new Base32();
byte[] bytes = base32.decode(secretKey);
String hexKey = Hex.encodeHexString(bytes);
return TOTP.getOTP(hexKey);
}
}
4. Create an account with Login.gov (one time operation)
a. Navigate to https://login.gov
b. Click on Sign in with LOGIN.GOV.
c. Click on Create an account and create an account.
d. You will be asked to add an authentication method. Feel free to use any approach, but I recommend using Text or voice message as we will reserve the Authentication application to be used for the Java code.
e. You will now be logged in to your Login.gov account, and on the left hand side, click Your authentication methods. You will see how a phone number has been set up, but not an authentication app.
f. Click on Add authentication apps.
g. Give it a nickname (can be anything, it will not be used anywhere) (e.g., JCode).
h. Copy the code that in the silver box and paste it in your Java code.
String secretKey = "PYYOO2RHNGSPMYXR3XXQTIACXDDSK4AT";
i. Run the Java code, and copy the output shown. The output is the temporary one-time code.
j. Paste this temporary code in the Login.gov window and click Submit.
k. The new authentication method is now set up, and your Java code is now registered as an authentication app in your Login.gov account.
5. Run the Java code
a. Uncomment the next section in the Java code under the main() method, then run the code.
The Chrome instance will authentication, generate and populate the one-time code, and login to the application.
References