The official Cypress documentation outlines an approach for programmatic OKTA authentication.

Why authenticate programmatically?

Typically, logging in a user within your app by authenticating via a third-party provider requires visiting login pages hosted on a different domain. Since each Cypress test is limited to visiting domains of the same origin, we can subvert visiting and testing third-party login pages by programmatically interacting with the third-party authentication API to login a user.

But for several cases the OKTA account requires MFA (Multi-Factor Authentication) due to company/organization security policy. This can create challenges in the login process during automated testing.

To pass the MFA auth in your test, we need to:

  1. Consult your security team to determine if MFA can be temporarily disabled for test accounts.
  2. If MFA cannot be disabled, configure it to use Security Questions (view all OKTA MFA options here). This allows automation by providing predefined answers in test cases.
  3. Fetch the stateToken with UserName/Password
  4. Submit the MFA Answers together with stateToken
  5. Fetch the sessionToken
  6. Set sessionToken via okta lib
  7. Get the ID Token + Access Token

The key step is to get the sessionToken with MFA answers (see OKTA API here)

Lets see how I do the command:


Cypress.Commands.add('loginByOktaApi', (username = 'testaccount', password = 'testaccount_1234') => {
  /**
   * Visit the homepage before initiating OKTA authentication to prevent postMessage errors 
   * @see https://github.com/cypress-io/cypress/issues/16310#issuecomment-1279523540
   */
  cy.visit('https://localhost:3000');

  return cy
    .request({
      method: 'POST',
      url: `https://${process.env.OKTA_DOMAIN}/api/v1/authn`,
      body: {
        username,
        password
      }
    })
    .then(({ body }) => {
      const user = body._embedded.user;
      const stateToken = body.stateToken;
      const factorId = body._embedded.factors[0].id;

      cy.request({
        method: 'POST',
        url: `https://${process.env.OKTA_DOMAIN}/api/v1/authn/factors/${factorId}/verify`,
        body: {
          stateToken,
          answer: process.env.OKTA_MFA_ANSWER
        }
      }).then(({ body }) => {
        const sessionToken = body.sessionToken;
        const config = {
          issuer: `https://${process.env.OKTA_DOMAIN}/oauth2/default`,
          clientId: process.env.OKTA_CLIENT_ID,
          redirectUri: 'https://localhost:3000/auth/callback',
          scope: ['openid', 'email', 'profile']
        };

        const authClient = new OktaAuth(config);

        return authClient.token.getWithoutPrompt({ sessionToken }).then(({ tokens }) => {
          window.localStorage.setItem('okta-token-storage', JSON.stringify(tokens));
        });
      });
    });
});

Next Step

Consider using cy.session() to persist authentication across tests, reducing login overhead. Note that this is still an experimental feature as of December 2022, and configuration adjustments may be required.