---
version: "2026"
language: "en"
---
# Use TOTP-Based MFA for Authenticated Flows in Leapwork Go

This article explains how to use time-based one-time password (TOTP) MFA in Leapwork Go for authenticated test flows. It also shows how to use the same MFA secret in a custom Auth Script when you need a Playwright-based login flow.

## What this article covers

* Using a TOTP secret with the built-in authentication flow.

* Using the same secret in a custom Auth Script.

* When to use the default flow and when to use Playwright.

## Before you start

* Your application must use TOTP-based MFA.

* You must have access to the shared secret key for the test account. Leapwork Go generates one-time codes from that secret.

* The secret must be provided in Base32 format.

* You need an AD User data item for the account that will authenticate.

* If your login flow is not covered by the built-in authentication flow, create an Auth Script data item.

> This feature supports TOTP-based MFA. Push approvals, SMS codes, email codes, and other non-TOTP MFA methods are not covered by this flow.

## How MFA works in Leapwork Go

Leapwork Go does not read a live code from your authenticator app. Instead, it generates a valid six-digit TOTP code from the same shared secret that was used to enroll the account.

There are two ways to use this capability:

1. The built-in authentication flow handles the MFA step automatically for supported standard Microsoft sign-in flows.

2. A custom Auth Script lets you handle the MFA step yourself in Playwright when the login flow is different or more complex.

## Step 1: Store the TOTP secret on the user

1. Open your project and go to **Data items**.

2. Open the **AD User** data item used for the authenticated flow.

3. Add the user account you want Leapwork Go to authenticate with.

4. Enter the account details, including the **TOTP Secret**.

5. Save the data item.

Use a dedicated test account. The secret should match the same account that is enrolled in your authenticator app or identity provider.

## Step 2: Use the built-in MFA flow

If your application uses the supported built-in authentication path, Leapwork Go can detect the MFA prompt and submit the generated code automatically.

Use this option when:

* your login flow follows the standard built-in Microsoft sign-in experience

* you only need username, password, and TOTP

* you do not need custom redirects, pop-ups, or non-standard selectors

Once the user is selected for the run, Leapwork Go uses the stored TOTP secret only when an MFA prompt is encountered.

## Step 3: Use MFA in a custom Auth Script

Use a custom Auth Script when your application uses a different sign-in experience, custom identity provider, extra redirects, pop-ups, or non-standard MFA screens.

Leapwork Go supports Playwright-based Auth Scripts directly.

To use an Auth Script:

1. Create or open an **Auth Script** data item.

2. Save your Playwright-based login logic in that data item.

3. Select the script in the **Auth Script** field on the relevant preview run or timeline item.

4. Select the AD User whose credentials and TOTP secret should be used for the run.

In an Auth Script, these values are available at runtime:

* `{{email}}`

* `{{password}}`

* `{{url}}`

* `{{totpSecret}}`

The script also exposes:

* `page`

* `context`

* `generateTotpCode()`

`generateTotpCode()` uses the TOTP secret configured for the selected AD User. In most cases, you can call it without arguments.

### Example Playwright MFA snippet

    await page.goto('{{url}}');
    await page.locator("input[type='email']").fill('{{email}}');
    await page.locator("input[type='submit']").click();
    await page.locator("input[type='password']").fill('{{password}}');
    await page.locator("input[type='submit']").click();
    try {
      await page.locator("input[name='otc']").waitFor({ timeout: 5000 });
      const code = generateTotpCode();
      await page.locator("input[name='otc']").fill(code);
      await page.locator("input[type='submit']").click();
    } catch {
      // No TOTP prompt was shown
    }
    await page.waitForURL('{{url}}');
    const cookies = await context.cookies();
    return cookies.map(c => `${c.name}=${c.value}`).join(';');

Adjust the locators to match your own sign-in page. The example above shows the pattern, not a universal selector set.

## Returning data from an Auth Script

If you do **not** select an output dictionary for the Auth Script, return the authenticated session data directly, for example a cookie string.

If you **do** select an output dictionary, return an object and Leapwork Go will merge that output into the selected dictionary instead of using it as request cookies.

## Diagnostic helpers in custom authentication scripts

Custom authentication scripts support three built-in helper functions: `log()`, `screenshot()`, and `fail()`. These functions provide visibility into what occurs during each authentication attempt and integrate directly with the existing authentication log displayed in preview runs.

### log()

    log(message: string)

Appends a custom message to the authentication execution log. Use `log()` to record progress milestones, variable states, or contextual notes at any point in the script.

    log("Navigating to tenant login page");
    await page.goto('{{url}}');
    log("Login page loaded --- entering credentials");

Log messages appear in the authentication log alongside the platform's own execution entries, in the order they were emitted.

### screenshot()

    screenshot(label: string)

Captures a screenshot of the current browser state and attaches it to the authentication log under the specified label. Use `screenshot()` to capture visual evidence of the page at key stages --- particularly before and after actions that could fail.

    await screenshot("login_form_loaded");
    await page.locator("input[type='email']").fill('{{email}}');
    await screenshot("after_email_entry");

**Limit:** A maximum of **50 screenshots** can be captured per authentication session. If the limit is reached, subsequent `screenshot()` calls fall back to a `log()` entry instead. Plan screenshot usage accordingly in scripts that iterate or retry frequently.

### fail()

    fail(errorMessage: string)

Immediately terminates the current authentication attempt with a failure status and records the provided message in the authentication log. Calling `fail()` triggers the same retry flow that fires on an unhandled exception the difference is that the failure reason is explicit and readable rather than a generic stack trace.

Use `fail()` inside `catch` blocks to ensure the platform's retry mechanism fires with a clear, human-readable reason.

    fail("MFA prompt did not appear within the expected timeout");

### Combined diagnostic pattern

The following example shows all three helpers working together in a typical custom authentication script. This is the recommended structure for scripts where clear failure evidence is required.

    try {
      log("Starting authentication for {{email}}");
      await page.goto('{{url}}');
      await screenshot("pre_login_state");
      await page.locator("input[type='email']").fill('{{email}}');
      await page.locator("input[type='submit']").click();
      log("Email submitted --- waiting for password field");
      await page.locator("input[type='password']").fill('{{password}}');
      await page.locator("input[type='submit']").click();
      log("Password submitted --- checking for MFA prompt");
      try {
        await page.locator("input[name='otc']").waitFor({ timeout: 5000 });
        const code = generateTotpCode();
        await page.locator("input[name='otc']").fill(code);
        await page.locator("input[type='submit']").click();
        log("TOTP code submitted");
      } catch {
        log("No MFA prompt detected --- continuing without TOTP step");
      }
      await page.waitForURL('{{url}}');
      await screenshot("post_login_state");
      log("Authentication completed successfully");
      const cookies = await context.cookies();
      return cookies.map(c => `${c.name}=${c.value}`).join(';');
    } catch (error) {
      await screenshot("authentication_failure");
      fail(`Authentication failed: ${error.message}`);
    }

### When to use each helper

|                         Scenario                         |       Recommended helper        |
|----------------------------------------------------------|---------------------------------|
| Record which step the script has reached                 | `log()`                         |
| Capture the page state before a likely failure point     | `screenshot()`                  |
| Capture the page state after a failure has occurred      | `screenshot()`                  |
| Signal a known, unrecoverable failure explicitly         | `fail()`                        |
| Full diagnostics for complex or intermittent login flows | All three, inside a `try/catch` |

## When to use Playwright instead of the default flow

Use a custom Auth Script when your sign-in experience includes one or more of these conditions:

* a non Microsoft Entra identity provider

* additional redirects or consent screens

* login pop-ups or custom browser flows

* custom MFA locators or field names

* a need to control the login steps more precisely than the default flow allows

## Validation

After setup, validate the configuration with a preview run or timeline run:

1. Select the AD User that has the TOTP secret configured.

2. Start the run.

3. Confirm that authentication completes without manual entry of the MFA code.

4. Confirm that the authenticated requests continue with a valid session.

For a custom Auth Script, also confirm that:

* the script reaches the expected post-login page

* the script returns the expected session data

* the MFA step succeeds only when the prompt is shown

## Troubleshooting

* If the run fails with a missing TOTP secret error, confirm that the AD User entry was created with a valid secret.

* If the generated code is rejected, confirm that the secret belongs to the same account that is being used for authentication and that the secret is in Base32 format.

* If the MFA screen looks different from the standard login flow, use a custom Auth Script and update the locators to match your page.

* If your identity provider does not expose a reusable secret key for TOTP enrollment, this feature cannot generate codes for that account.

* If a saved AD User entry was created without a TOTP secret, create a new user entry with the secret included and use that entry for the run.

* If a custom Auth Script fails with no useful detail in the log, add `log()` calls at key points in the script to trace which step was reached before the failure.

* If the authentication log shows a generic error with no visual context, add `screenshot()` calls before and after the action that is failing to capture the browser state at the time of the error.