MFA

Prev Next

MFA, short for Multi-Factor Authentication, is an electronic authentication method that requires users to provide at least two forms of authentication to access a system or application. One-Time Passwords (OTP), Time-based One Time Password (TOTP), and Hash-based One Time Passwords (HOTP) are all forms of MFA commonly used by organizations.

By requiring multiple factors for authentication, MFA makes it more difficult for unauthorized users to gain access to sensitive information or systems, significantly enhancing security. However, MFA can also pose challenges for testing applications from a user's perspective. Disabling MFA temporarily for testing purposes is an option, but organizations may need to verify that MFA modules are working correctly and do not negatively impact the user experience. Catchpoint's Transaction Test type supports TOTP authentication, enabling organizations to test MFA modules effectively using industry-standard Selenium and Playwright scripting frameworks.

Prerequisites

To test MFA with Catchpoint, you must first capture the Secret Key from your authentication system and add it to the Credentials Library.

Capturing the Secret Key

Okta

For Okta SSO, when configuring the OKTA OTP settings, it generates the Account Secret, which you would copy and add to the Credentials Library for use in your scripts.
image.png

MIcrosoft O365 / Azure

For MS O365/ Azure SSO, the same secret key needs to be generated. Teams would have it for service accounts.

The following screenshots show how to generate the key in office.com account settings by adding a new sign-in method.
image.png
image.png
image.png

Storing the Key in Credentials Library

Once you have your Secret Key, it needs to be captured and stored in the Credentials Library, from which it will eventually be passed to your Transaction test scripts. For complete instructions on using the Credentials Library, see this article.

In this example, we are storing this key in the Credentials Library using the token name accountSecret.
image.png

Configuring a Transaction Test for MFA

Once the secret key is stored in the Credentials Library, you can configure a Transaction Test to use the key for MFA testing. The following sections provide instructions for configuring the test and a sample Transaction Script.

Referencing the Stored Key

In order to use the stored key in your transaction script, you must reference it in your Test configuration by adding it under the Requests section.
image.png

Sample Playwright Script

The following sample Playwright script uses a stored secret key to perform MFA. Read the instructions below the script to understand how it functions.

// Step - 1
await Catchpoint.startStep("Initiating Variables");

const myuserid = await Catchpoint.username("ABC");
const mypassword = await Catchpoint.password("XYZ");
const accountSecret = '#######';

// Step - 2
await Catchpoint.startStep("Navigating to Portal");

await page.goto('https://xyz.com', {
  waitUntil: 'domcontentloaded'
});


// Step - 3
await Catchpoint.startStep("Step 2 - Enter Username");
await page.waitForSelector("input[name='username']", { timeout: 15000 });
await page.fill("input[name='username']", myuserid);

// Wait for password field to appear (transition validation)
await page.waitForSelector("input[name='password']", { timeout: 15000 });

// Step - 4
await Catchpoint.startStep("Step 3 - Enter Password");
await page.fill("input[name='password']", mypassword);

// Step - 5
await Catchpoint.startStep("Generate  TOTP");

await page.waitForSelector("input[name='totp']", { timeout: 15000 });


const otp = await page.evaluate((accountSecret) => {

  // 2. Hashing function
  !function(){var t=function(t){var r,n=[],e=8*t.length;for(r=0;r<e;r+=8)n[r>>5]|=(255&t.charCodeAt(r/8))<<24-r%32;return n},r=function(t){var r,n,e=[],s=t.length;for(r=0;r<s;r+=2){if(n=parseInt(t.substr(r,2),16),isNaN(n))return'INVALID HEX STRING';e[r>>3]|=n<<24-r%8*4}return e},n=function(t){var r,n,e='0123456789abcdef',s='',i=4*t.length;for(r=0;r<i;r+=1)n=t[r>>2]>>8*(3-r%4),s+=e.charAt(n>>4&15)+e.charAt(15&n);return s},e=function(t){var r,n,e,s='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',i='',u=4*t.length;for(r=0;r<u;r+=3)for(e=(t[r>>2]>>8*(3-r%4)&255)<<16|(t[r+1>>2]>>8*(3-(r+1)%4)&255)<<8|t[r+2>>2]>>8*(3-(r+2)%4)&255,n=0;n<4;n+=1)8*r+6*n<=32*t.length?i+=s.charAt(e>>6*(3-n)&63):i+='';return i},s=function(t,r){return t<<r|t>>>32-r},i=function(t,r,n){return t^r^n},u=function(t,r,n){return t&r^~t&n},a=function(t,r,n){return t&r^t&n^r&n},h=function(t,r){var n=(65535&t)+(65535&r);return(65535&(t>>>16)+(r>>>16)+(n>>>16))<<16|65535&n},c=function(t,r,n,e,s){var i=(65535&t)+(65535&r)+(65535&n)+(65535&e)+(65535&s);return(65535&(t>>>16)+(r>>>16)+(n>>>16)+(e>>>16)+(s>>>16)+(i>>>16))<<16|65535&i},f=function(t,r){var n,e,f,o,l,E,N,T,H,I=[],A=u,g=i,S=a,O=s,v=h,B=c,C=[1732584193,4023233417,2562383102,271733878,3285377520],R=[1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1518500249,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,1859775393,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,2400959708,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782,3395469782];for(t[r>>5]|=128<<24-r%32,t[15+(r+65>>9<<4)]=r,H=t.length,N=0;N<H;N+=16){for(n=C[0],e=C[1],f=C[2],o=C[3],l=C[4],T=0;T<80;T+=1)I[T]=T<16?t[T+N]:O(I[T-3]^I[T-8]^I[T-14]^I[T-16],1),E=B(O(n,5),T<20?A(e,f,o):T<40?g(e,f,o):T<60?S(e,f,o):g(e,f,o),l,R[T],I[T]),l=o,o=f,f=O(e,30),e=n,n=E;C[0]=v(n,C[0]),C[1]=v(e,C[1]),C[2]=v(f,C[2]),C[3]=v(o,C[3]),C[4]=v(l,C[4])}return C},o=function(n,e){if(this.sha1=null,this.strBinLen=null,this.strToHash=null,'HEX'===e){if(0!=n.length%2)return'TEXT MUST BE IN BYTE INCREMENTS';this.strBinLen=4*n.length,this.strToHash=r(n)}else{if('ASCII'!==e&&void 0!==e)return'UNKNOWN TEXT INPUT TYPE';this.strBinLen=8*n.length,this.strToHash=t(n)}};o.prototype={getHash:function(t,r){var s=null,i=this.strToHash.slice();switch(r){case'HEX':s=n;break;case'B64':s=e;break;default:return'FORMAT NOT RECOGNIZED'}switch(t){case'SHA-1':return null===this.sha1&&(this.sha1=f(i,this.strBinLen)),s(this.sha1);default:return'HASH NOT RECOGNIZED'}},getHMAC:function(s,i,u,a){var h,c,o,l,E,N,T,H,I,A=[],g=[];switch(a){case'HEX':h=n;break;case'B64':h=e;break;default:return'FORMAT NOT RECOGNIZED'}switch(u){case'SHA-1':o=64,I=160;break;default:return'HASH NOT RECOGNIZED'}if('HEX'===i){if(0!=s.length%2)return'KEY MUST BE IN BYTE INCREMENTS';c=r(s),H=4*s.length}else{if('ASCII'!==i)return'UNKNOWN KEY INPUT TYPE';c=t(s),H=8*s.length}for(l=8*o,T=o/4-1,o<H/8?('SHA-1'===u&&(c=f(c,H)),c[T]&=4294967040):o>H/8&&(c[T]&=4294967040),E=0;E<=T;E+=1)A[E]=909522486^c[E],g[E]=1549556828^c[E];return'SHA-1'===u&&(N=f(A.concat(this.strToHash),l+this.strBinLen),N=f(g.concat(N),l+I)),h(N)}},eval('window').jsSHA=o}();

  // 3. Calculate OTP
  var ff,dec2hex=function(e){return(e<15.5?'0':'')+Math.round(e).toString(16)},
  hex2dec=function(e){return parseInt(e,16)},
  base32tohex=function(e){for(var t='',n='',r=0;r<e.length;r++){var a='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(e.charAt(r).toUpperCase());t+=leftpad(a.toString(2),5,'0')}for(r=0;r+4<=t.length;r+=4){var f=t.substr(r,4);n+=parseInt(f,2).toString(16)}return n},
  leftpad=function(e,t,n){return t+1>=e.length&&(e=new Array(t+1-e.length).join(n)+e),e},
  generate=function(e,t){var n=base32tohex(e);n.length%2!=0&&(n+='0'),void 0===t&&(t=Math.round((new Date).getTime()/1e3));var r=leftpad(dec2hex(Math.floor(t/30)),16,'0'),a=new jsSHA(r,'HEX').getHMAC(n,'HEX','SHA-1','HEX'),f=0;'KEY MUST BE IN BYTE INCREMENTS'!==a&&(f=hex2dec(a.substring(a.length-1)));var o=(hex2dec(a.substr(2*f,8))&hex2dec('7fffffff'))+'';return ff=o.substr(o.length-6,6).toString()};

  var finalOTP = generate(accountSecret);
  return finalOTP;

}, accountSecret);

console.log('OTP:', otp);

// Step - 6
await Catchpoint.startStep("Submitting TOTP");
await page.fill("input[name='totp']", otp);
await page.click("input[name='submit']");

Step 1: Pass the Account Secret Here

const accountSecret = '#######';

Replace '##########' with your actual account secret. This secret is the input for generating the OTP.

Step 2: Hashing Function

(function () {
 // Hashing functions and other utilities are defined here
 // ...
 })();

This code defines a self-executing function that contains various utility functions and the core logic for hashing the account secret and generating the OTP.

Step 3: Generate OTP

var ff; var
dec2hex=function(e){return(e<15.5?'0':'')+Math.round(e).toString(16)},hex2dec=function(e){return parseInt(e,16)},base32tohex=function(e){for(var t='',n='',r=0;r<e.length;r++){var a='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(e.charAt(r).toUpperCase());t+=leftpad(a.toString(2),5,'0')}for(r=0;r+4<=t.length;r+=4){var f=t.substr(r,4);n+=parseInt(f,2).toString(16)}return n},leftpad=function(e,t,n){return t+1>=e.length&&(e=new Array(t+1-e.length).join(n)+e),e},generate=function(e,t){var n=base32tohex(e);n.length%2!=0&&(n+='0'),void 0===t&&(t=Math.round((new Date).getTime()/1e3));var r=leftpad(dec2hex(Math.floor(t/30)),16,'0'),a=new jsSHA(r,'HEX').getHMAC(n,'HEX','SHA-1','HEX'),f=0;'KEY MUST BE IN BYTE INCREMENTS'!==a&&(f=hex2dec(a.substring(a.length-1)));var o=(hex2dec(a.substr(2*f,8))&hex2dec('7fffffff'))+'';ff=o.substr(o.length-6,6).toString();return ff;}; var finalOTP = generate(accountSecret);

This part of the JavaScript code generates a One-Time Password (OTP) using the Time-Based One-Time Password (TOTP) algorithm. Here's a breakdown of what the code does step by step:

  1. Importing Required Functions:
    • The code defines several utility functions: dec2hex, hex2dec, base32tohex, leftpad, and generate. These functions will be used in the OTP calculation.
  2. base32tohex Function:
    • This function converts a base32-encoded string (commonly used for secrets in TOTP) into its hexadecimal representation.
    • It iterates through each character in the input string, finds its binary representation (5 bits for each character), and appends them together.
    • Then, it converts the binary string into a hexadecimal string.
  3. generate Function:
    • This is the main function for generating the OTP.
    • It takes two arguments: e (accountSecret) and an optional t (current time in seconds since epoch). If t is not provided, it defaults to the current time.
    • It first converts the accountSecret (which should be a base32-encoded string) into a hexadecimal representation using the base32tohex function.
    • It calculates the time step by dividing the current time t by 30 seconds (the default time step for TOTP) and converts it to a 16-character zero-padded hexadecimal string.
    • It then uses the jsSHA library to calculate an HMAC-SHA1 hash of the time step using the accountSecret as the key.
    • The hash result is processed to determine the starting index for extracting an 8-character substring.
    • The extracted substring is converted to a decimal integer, and bitwise AND operation is applied with '7fffffff'.
    • Finally, the last 6 digits of this value are extracted, converted to a string, and stored in the ff variable.

Step 4: Enter OTP

The final OTP value (ff) is set in the OTP field using document.querySelector(‘[input[name=totp]’).value = otp;.
The above JavaScript selector would vary depending on the field attributes. Below are the details with an example for JavaScript selector.

In JavaScript, a selector is a way to target and retrieve HTML elements from a web page so that you can manipulate or interact with them programmatically.

Here's a short explanation and an example HTML snippet:

<html>
    <head>
    <title>Example Page</title>
    </head>
    <body>
        <input type="text" id="OTP_field" value="123456">
    </body>
</html>

In this example, there is an <input> element with the id attribute set to "OTP_field." You can use JavaScript to select this input element by its ID and perform operations on it. For instance, you could change its value or listen for user input in the field. Here's how you would select it using JavaScript:

var otpInput = document.getElementById("OTP_field");

// Now, you can access or manipulate the otpInput element
otpInput.value = "654321"; // Changes the input's value

In the code above, otpInput now contains a reference to the input element with the ID "OTP_field," allowing you to work with it in your JavaScript code.

NOTE:

In some cases there is a need to fire events to ensure the OTP is accepted.

External reference: https://blog.bitsrc.io/dom-selectors-explained-70260049aaf0

The selenium version of the same script can be found in this attachment.
MFA- Selenium.txt