I have an MFA set up on login page of the web portal I am testing, and Katalon seems to handle MFA using Aerogear external library to fetch OTP.
Can anyone tell me a better approach to implement MFA handling into Katalon script?
To handle Multi-Factor Authentication (MFA) via OAuth in Katalon Studio, use one of the following robust approaches:
1. Generate TOTP Codes Programmatically
Use Case: When you have access to the MFA secret key (e.g., for time-based OTP apps like Google Authenticator).
Steps:
- Get the MFA Secret Key: Obtain the base32-encoded secret (e.g., from your MFA setup QR code).
- Add the
TOTP
Library: Use Java libraries likejava-otp
. - Generate the OTP in your test script:
import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator
import java.time.Instant
import javax.crypto.spec.SecretKeySpec
// Define your MFA secret (base32 encoded)
String mfaSecret = "YOUR_BASE32_SECRET"
// Generate the TOTP code
def secretKey = new SecretKeySpec(mfaSecret.decodeBase32(), "HmacSHA1")
def totpGenerator = new TimeBasedOneTimePasswordGenerator()
String otpCode = totpGenerator.generateOneTimePassword(secretKey, Instant.now()).toString()
// Enter the OTP code into the MFA field
WebUI.setText(findTestObject('OTP_Input_Field'), otpCode)
2. Bypass MFA via API Token
Use Case: When the system allows skipping MFA by using an OAuth token obtained via API.
Steps:
- Get an Access Token using a REST API call (e.g., OAuth2 client credentials grant):
@Keyword
String getOAuthToken() {
def response = WS.sendRequestAndVerify(
findTestObject('Auth_API_Endpoint', [
('client_id'): GlobalVariable.clientId,
('client_secret'): GlobalVariable.clientSecret
])
)
return WS.getElementPropertyValue(response, 'access_token')
}
- Inject the Token into the browser session to skip MFA:
// Set the token in local storage or cookies
WebUI.executeJavaScript("localStorage.setItem('access_token', '${accessToken}')", null)
3. Reuse Authenticated Sessions
Use Case: When MFA is required only once per session.
Steps:
- Manually log in once and save the session cookies:
// After login, save cookies
def cookies = WebUI.getCookieNames()
WebUI.comment("Cookies: ${cookies}")
- Reuse cookies in subsequent tests:
// Restore cookies before navigating
WebUI.navigateToUrl(GlobalVariable.baseUrl)
WebUI.addCookie('session_id', 'YOUR_SESSION_ID')
WebUI.refresh()
4. Use Test Accounts with MFA Disabled
Use Case: When testing in a non-production environment.
Steps:
- Configure test accounts with MFA disabled.
- Use these credentials directly in Katalon:
WebUI.setText(findTestObject('Username_Field'), 'testuser@domain.com')
WebUI.setEncryptedText(findTestObject('Password_Field'), 'encryptedPassword')
WebUI.click(findTestObject('Login_Button'))
5. Integrate with Secrets Management Tools
Use Case: Securely handle MFA secrets or tokens.
Tools: HashiCorp Vault, AWS Secrets Manager.
Steps:
- Retrieve the MFA secret/token dynamically:
@Keyword
String getMfaSecretFromVault() {
def secret = WS.sendRequestAndVerify(findTestObject('Vault_API_Endpoint'))
return WS.getElementPropertyValue(secret, 'data/mfa_secret')
}
Best Practices
- Encrypt Secrets: Store MFA keys/tokens in Katalon’s Encrypted Execution or external vaults.
- Synchronize Clocks: Ensure the test machine’s clock is synchronized for TOTP to work.
- Use Profile-Specific Configs: Manage MFA settings via Katalon Profiles for different environments.
By implementing these methods, you can automate MFA scenarios efficiently without relying on external libraries like Aerogear
If you have the 32 character secret key you can use like below.
I’ve implemented this and works smoothly.
Create a custom keyword like below
import org.jboss.aerogear.security.otp.Totp
@Keyword
def GetMFATokenNow(secret) {
Totp totp = new Totp(secret)
return totp.now()
}
Then in your test case you can call this method which returns the OTP and send it to the desired input field.
You will have to provide the secret key at run time if you will be using for multiple email/userid OR you can store it in global variable and use if only 1 email is there.
def otp_now = GetMFATokenNow(secret)
WebUI.setText(findTestObject('Object_Path'), otp_now)