---
title: "Automate MFA Testing with Cypress — Tutorial, Code & Best Practices"
description: "Automate SMS-based MFA in Cypress tests with a custom command that calls the GetMyMFA API. Setup, TypeScript code, demo project and best practices."
url: "https://get.mymfa.io/blog/cypress-e2e-mfa"
canonical: "https://get.mymfa.io/blog/cypress-e2e-mfa"
markdown: "https://get.mymfa.io/blog/cypress-e2e-mfa.md"
author: "Jonathan Bernales"
author_url: "https://get.mymfa.io/about#jonathan-bernales"
publisher: "GetMyMFA"
date_published: "2026-02-14"
date_modified: "2026-02-14"
category: "Automation"
tags:
  - "Cypress"
  - "Multi-Factor Authentication"
  - "E2E Test Automation"
hub: "https://get.mymfa.io/features/test-automation"
plans: "Pro, Enterprise"
---

# Automate MFA Testing with Cypress — Tutorial, Code & Best Practices

*By Jonathan Bernales, 2026-02-14. Canonical: https://get.mymfa.io/blog/cypress-e2e-mfa*

> **TL;DR** To automate MFA testing with Cypress, use GetMyMFA's API to fetch SMS verification codes during your E2E test. Create a custom Cypress command that calls the /v1/{phoneId}/mfa/latest endpoint, extracts the mfaCode, and types it into the verification input using cy.get and cy.type.

## Introduction & Context

If you are running **end-to-end tests with Cypress** and your application uses **Multi-Factor Authentication (MFA)**, you have probably run into the same wall as everyone else: your automated tests stop at the MFA screen. Some teams disable MFA in their test environment ([a risky shortcut](https://get.mymfa.io/blog/test-automation-limits)), others go back to manual testing, and many simply skip testing MFA flows altogether. Everything below relies on the [GetMyMFA MFA test automation API](https://get.mymfa.io/features/test-automation), available on the Pro and Enterprise plans.

None of these options are great. In this tutorial, we will walk through a **clean, production-ready approach** to automating SMS-based MFA with **Cypress** and the **GetMyMFA** API. By the end, you will have a working test that **logs in, retrieves the MFA code programmatically, and completes authentication**, all without touching a phone.

We previously covered this same workflow using [Playwright](https://get.mymfa.io/blog/playwright-e2e-mfa). If you are using Cypress instead, this guide is for you. The same pattern can also be applied to other critical flows like financial transactions or sensitive approvals.

#### What we will build

A **Cypress E2E test** that handles the full SMS-based MFA login flow automatically. Here is what the test does:

- Opens the login page and enters credentials
- Waits for the MFA prompt to appear
- Calls the GetMyMFA API to retrieve the latest verification code
- Fills in the code and submits
- Verifies that the login succeeded

*Complete source code is available for download at the end of this article.*

## Overview: How the flow works

Whether you use Cypress, Playwright, or any other framework, the high-level flow is the same. Here is how the pieces fit together:

1. **Start sign-in.** Your Cypress test navigates to the login page and enters the user credentials.
2. **App sends SMS.** The application sends an MFA code by SMS to a **GetMyMFA virtual number**.
3. **Code exposed via API.** GetMyMFA captures the SMS and makes the code available through its API.
4. **Fetch the code.** Your test calls the API using `cy.request()` to get the latest code.
5. **Submit the code.** The test types the code into the MFA form and completes authentication.

![Macro process: Cypress triggers login, app sends SMS, GetMyMFA API returns code, test submits MFA](https://get.mymfa.io/_next/static/immutable/media/1_macro-flow.2m3elu1utr-xc.png)

*Note: In this demo, the actual SMS send (step 2) is skipped to avoid cost; the rest remains fully automated.*

## Tutorial: Set up Cypress + GetMyMFA

#### Step 1 — Install Cypress

If you already have a project, install Cypress as a dev dependency:

```bash
npm install cypress --save-dev
```

Then open Cypress for the first time to generate the default folder structure:

```bash
npx cypress open
```

Select **E2E Testing** in the welcome screen, pick a browser, and Cypress will scaffold the following folders:

```text
my-project/
      ├── cypress/
      │   ├── e2e/          ← your test files go here
      │   ├── fixtures/     ← static test data
      │   └── support/
      │       ├── commands.ts  ← custom commands (we will add one)
      │       └── e2e.ts       ← runs before every spec
      ├── cypress.config.ts    ← project configuration
      └── package.json
```

If you are starting from scratch, create a new folder and run `npm init -y` first, then install Cypress.

You will also need `dotenv` to load environment variables:

```bash
npm install dotenv --save-dev
```

#### Step 2 — Create a GetMyMFA test account + API key

Head to [get.mymfa.io](https://get.mymfa.io) and click *Sign Up*. The **Trial plan** gives you a **temporary virtual phone number with full API access**.

Once signed in, go to **API Configuration** and create an API key. Copy it somewhere safe — you will need it in the next steps.

![GetMyMFA dashboard - API configuration](https://get.mymfa.io/_next/static/immutable/media/4_gmm_image.1gmei2jjuqny4.png)

#### Step 3 — Get your virtual phone number ID

Using your API key as an `x-api-key` header, call the following endpoint:

```http
GET https://programmatic-api.client.get.mymfa.io/v1/phone-numbers
```

The response includes a `phoneId` field. Save this value — your test will use it to retrieve MFA codes. You can also verify everything works by calling:

```http
GET https://programmatic-api.client.get.mymfa.io/v1/<YOUR_PHONE_ID>/mfa/latest
```

The code is in the `mfaCode` field of the response.

![Postman call to list phone numbers](https://get.mymfa.io/_next/static/immutable/media/5_postman1.3yiael2czr3-j.png)

Here is what the second call looks like when you fetch the latest MFA code:

![Postman call to get latest MFA code](https://get.mymfa.io/_next/static/immutable/media/6_postman2.0rlpcivt-4if1.png)

#### Step 4 — Configure environment variables

Create a `.env` file in the root of your project with your credentials:

```dotenv
GETMYMFA_PHONE_NUMBER_ID=your_phone_id_here
      GETMYMFA_API_KEY=your_api_key_here
      TEST_USER_EMAIL=your_test_email
      TEST_USER_PASSWORD=your_test_password
```

Then update your `cypress.config.ts` to load them into the Cypress environment:

```ts
import { defineConfig } from 'cypress'
import * as dotenv from 'dotenv'
dotenv.config()

export default defineConfig({
  e2e: {
    baseUrl: 'https://demo.mymfa.io',
    setupNodeEvents(on, config) {
      config.env = {
        ...config.env,
        GETMYMFA_PHONE_NUMBER_ID: process.env.GETMYMFA_PHONE_NUMBER_ID,
        GETMYMFA_API_KEY: process.env.GETMYMFA_API_KEY,
        TEST_USER_EMAIL: process.env.TEST_USER_EMAIL,
        TEST_USER_PASSWORD: process.env.TEST_USER_PASSWORD,
      }
      return config
    },
  },
})
```

This approach keeps secrets out of your code. The `setupNodeEvents` function runs in Node.js (not the browser), so it can safely read `process.env` values and inject them into `Cypress.env()`.

> Make sure
> 
> .env
> 
> is listed in your
> 
> .gitignore
> 
> so you never commit secrets to version control.

#### Step 5 — Add a custom command to fetch the MFA code

Cypress has a powerful feature called **custom commands** that lets you encapsulate reusable logic. We will create a `getMfaCode` command that calls the GetMyMFA API using `cy.request()`:

```ts
// cypress/support/commands.ts

Cypress.Commands.add('getMfaCode', () => {
  const phoneId = Cypress.env('GETMYMFA_PHONE_NUMBER_ID')
  const apiKey = Cypress.env('GETMYMFA_API_KEY')

  return cy.request({
    method: 'GET',
    url: `https://programmatic-api.client.get.mymfa.io/v1/${phoneId}/mfa/latest`,
    headers: {
      'x-api-key': apiKey,
      'Content-Type': 'application/json',
    },
  }).then((response) => {
    return response.body.mfaCode
  })
})
```

`cy.request()` is Cypress's built-in way to make HTTP calls. Unlike `fetch` or `axios`, it is fully integrated into the Cypress command chain, which means it automatically waits, retries, and logs in the Cypress test runner.

If you are using TypeScript, add a type declaration so your editor recognizes the new command:

```ts
// cypress/support/index.d.ts

declare namespace Cypress {
  interface Chainable {
    getMfaCode(): Chainable<string>
  }
}
```

#### Step 6 — Write the E2E test

Create a new file at `cypress/e2e/mfa-login.cy.ts` with the following test:

```ts
describe('Login with SMS-based MFA', () => {
  it('completes the full MFA login flow', () => {
    // Navigate to the demo login page
    cy.visit('/login')
    cy.title().should('include', 'GetMyMFA')

    // Fill in login fields and start sign-in
    cy.get('[name="email"]').type(Cypress.env('TEST_USER_EMAIL'))
    cy.get('[name="password"]').type(Cypress.env('TEST_USER_PASSWORD'))
    cy.contains('button', 'Login').click()

    // Ensure MFA prompt is visible
    cy.contains('MFA Time').should('be.visible')

    // Fetch MFA code via GetMyMFA API and submit it
    cy.getMfaCode().then((mfaCode) => {
      cy.get('[name="mfaCode"]').type(mfaCode)
      cy.contains('button', 'Submit').click()
    })

    // Confirm login succeeded
    cy.contains('MFA code verified').should('be.visible')
  })
})
```

Let us walk through what this test does:

1. **`cy.visit('/login')`** — Opens the login page. The base URL is defined in `cypress.config.ts`, so you only need the path.
2. **`cy.get()` + `.type()`** — Finds the email and password fields by their `name` attribute and types the credentials.
3. **`cy.contains('button', 'Login').click()`** — Clicks the Login button. `cy.contains()` finds an element by its text content, scoped to buttons.
4. **`cy.getMfaCode()`** — Calls our custom command, which hits the GetMyMFA API and returns the code.
5. **`.then((mfaCode) => ...)`** — Once we have the code, we type it into the MFA field and submit.
6. **`cy.contains('MFA code verified')`** — Asserts that the login succeeded.

Run your test with:

```bash
npx cypress run --spec cypress/e2e/mfa-login.cy.ts
```

Or open the Cypress UI to watch it execute step by step:

```bash
npx cypress open
```

## Best Practices for MFA Test Automation

#### Security Considerations

- **Environment Isolation:** Use **dedicated test environments** with separate MFA configurations. Never use production phone numbers in tests.
- **API Key Management:** Store API keys as **environment variables or CI/CD secrets**, never hard-coded in your test files.

#### Test Reliability

- **Stable Selectors:** Use `data-testid` attributes or `name` attributes rather than CSS classes that may change. Cypress also supports [best practices for selecting elements](https://docs.cypress.io/guides/references/best-practices#Selecting-Elements).
- **Retry Logic:** If the MFA code is not immediately available (e.g., SMS delivery delay), add a small `cy.wait()` before calling the API, or implement a retry loop with `cy.request()` checks.

#### CI/CD Integration

- **Headless Mode:** Run `npx cypress run` in CI — Cypress runs headless by default in this mode.
- **Parallel Execution:** Use **unique virtual numbers** for parallel test runs to avoid MFA code collisions between tests.
- **Environment Variables:** Configure API credentials through your **CI provider's secret management** (GitHub Actions secrets, GitLab CI variables, etc.).
- **Artifacts:** Cypress automatically captures **screenshots on failure** and can record **videos** of test runs — enable these in CI for debugging.

## Final thoughts

Automating MFA testing with Cypress does not require complex workarounds. With the GetMyMFA API and a simple custom command, your tests can handle SMS-based verification codes just like any other form field.

The result is an **end-to-end test that mirrors real user behavior**, without disabling security features or resorting to manual steps. This gives your team **confidence that authentication works correctly** before every deployment.

Whether you are running tests locally or in a CI pipeline, this pattern scales cleanly. Set it up once, and every test run validates your MFA flow automatically.

## Next steps

- [Download Source Code](https://github.com/jbernales5/getmymfa-cypress-demo)
- [Sign up to GetMyMFA](https://get.mymfa.io/signup)
- [Get in Touch](mailto:hello@mymfa.io?subject=MFA Testing with Cypress - Question)

## Related

- Feature hub: [MFA test automation API](https://get.mymfa.io/features/test-automation)
- [Automate MFA Testing with Selenium — Tutorial, Code & Best Practices](https://get.mymfa.io/blog/selenium-e2e-mfa) ([markdown](https://get.mymfa.io/blog/selenium-e2e-mfa.md))
- [Automate MFA Testing with Playwright — Tutorial, Code & Best Practices](https://get.mymfa.io/blog/playwright-e2e-mfa) ([markdown](https://get.mymfa.io/blog/playwright-e2e-mfa.md))
- [Using webhooks to industrialize MFA Testing](https://get.mymfa.io/blog/webhook-feature) ([markdown](https://get.mymfa.io/blog/webhook-feature.md))

---

GetMyMFA: https://get.mymfa.io. All guides: https://get.mymfa.io/blog. Machine-readable index: https://get.mymfa.io/llms.txt
