Skip to content

Instantly share code, notes, and snippets.

@ndavis
Created November 7, 2019 03:38
Show Gist options
  • Save ndavis/2c84ab40aaa3c98c3a8062bdb3938232 to your computer and use it in GitHub Desktop.
Save ndavis/2c84ab40aaa3c98c3a8062bdb3938232 to your computer and use it in GitHub Desktop.
Cypress Custom Command for Okta Login
Cypress.Commands.add('loginOkta', () => {
const optionsSessionToken = {
method: 'POST',
url: Cypress.env('session_token_url'),
body: {
username: Cypress.env('username'),
password: Cypress.env('password'),
options: {
warnBeforePasswordExpired: 'true'
}
}
}
cy.request(optionsSessionToken).then(response => {
const sessionToken = response.body.sessionToken;
const qs = {
client_id: Cypress.env('client_id'),
code_challenge: Cypress.env('code_challenge'),
state: Cypress.env('state'),
nonce: Cypress.env('nonce'),
redirect_uri: Cypress.env('redirect_uri'),
code_challenge_method: 'S256',
response_mode: 'fragment',
response_type: 'code',
scope: ['openid', 'profile', 'email'],
sessionToken: sessionToken
}
cy.request({
method: 'GET',
url: Cypress.env('auth_token_url'),
form: true,
followRedirect: false,
qs: qs
}).then(responseWithToken => {
const redirectUrl = responseWithToken.redirectedToUrl;
const accessToken = redirectUrl
.substring(redirectUrl.indexOf('access_token'))
.split('=')[1]
.split('&')[0];
cy.wrap(accessToken).as('accessToken');
cy.visit(redirectUrl).then(() => {
cy.visit('/');
});
});
});
})
@cuadllop
Copy link

cuadllop commented Aug 19, 2020

Hi there

That workaround also started failing for me since last week. I haven't been able to find a reason why it stopped working.
The flow is slightly different as the solution provided by @ndavis

  1. Get a session token (same as before)
  2. Use the library okta-auth-js (npm i @okta/okta-auth-js) calling the method getWithoutPrompt to get the idToken and set the variable into the Okta authClient object.
var OktaAuth = require('@okta/okta-auth-js');
  
  var authClient = new OktaAuth({      
    url: 'https://YOUR_DOMAIN.okta.com',      
    clientId: Cypress.env('client_id'),      
    redirectUri: Cypress.env('testConfig').baseUrl + Cypress.env('redirect_uri')
  });
  
  // Attempt to retrieve ID Token from Token Manager

  const optionsSessionToken = {
    method: 'POST',
    url: Cypress.env('session_token_url'),
    body: {
      username: username,
      password: password,
      options: {
        warnBeforePasswordExpired: 'true'
      }
    }
  }
  var redirect_uri = "" + Cypress.env('testConfig').baseUrl + Cypress.env('redirect_uri');

  cy.log(redirect_uri);

  cy.request(optionsSessionToken).then(response => {
    const sessionToken = response.body.sessionToken;

    authClient.token.getWithoutPrompt({
      sessionToken: sessionToken,
      scopes: [
        'openid',
        'email',
        'profile'
      ],
      state: Cypress.env('state'),
      nonce: Cypress.env('nonce')
    })
      .then(function (res) {
        authClient.tokenManager.add('idToken', res);
      })
      .catch(function (err) {
        // handle OAuthError or AuthSdkError
      });
  })

  cy.visit('/')

I thought this was going to solve the issue in Firefox as well since it should be crossing domains; however, I am still getting the same error in Firefox as I was getting before.

Hope it helps.

@boda234baran
Copy link

Hi @cuadllop ! Did you decide this problem?

Maybe you may tell me where I get these options ?

  • code_challenge
  • nonce
  • state

Reviewed in all requests in authorization in okta and never came....

@cuadllop
Copy link

cuadllop commented Aug 26, 2020

Hi @boda234baran,

Yes, this solution is working for me and my team so far.

I got those details by analyzing the POST requests when doing a manual login into my app:
image

Bear in mind that each environment you have configured in okta will have its own set of properties.
Then it is configured in my env.json properties file
image

, I don't know the value of code_challenge. It works for me to configure it as empty
"code_challenge": "",

@keaoner
Copy link

keaoner commented Jun 3, 2021

Hi, I am newbie to cypress, how can I integrate this code for okta authentication into my existing tests on Cypress?

Thank you

@iamskok
Copy link

iamskok commented Feb 1, 2022

Here is the solution that worked for me. Two things to point out:

  • Your company might be using custom OKTA_AUTHORIZATION_SERVER_ID and not default that is used in the most doc examples
  • You should be able to use dummy values for state and nonce

Certain values (like scope) might be different for your Okta configuration, but you should be able to identify them by inspecting the network tab.

Cypress.Commands.add('oktaApiLogin', ({ email, password, url }) => {
  const optionsSessionToken = {
    method: 'POST',
    url: `${Cypress.env('OKTA_DOMAIN')}/api/v1/authn`,
    body: {
      username: email,
      password,
      options: {
        warnBeforePasswordExpired: 'true',
      },
    },
  };

  cy.request(optionsSessionToken).then((response) => {
    const { sessionToken } = response.body;
    cy.log(`sessionToken: ${sessionToken}`);

    const qs = {
      response_type: 'code',
      client_id: Cypress.env('OKTA_CLIENT_ID'),
      state: 'test-state',
      nonce: 'test-nonce',
      redirect_uri: Cypress.env('OKTA_REDIRECT_URI'),
      scope: 'openid offline_access email',
      sessionToken,
    };

    cy.request({
      method: 'GET',
      url: `${Cypress.env('OKTA_DOMAIN')}/oauth2/${Cypress.env(
        'OKTA_AUTHORIZATION_SERVER_ID'
      )}/v1/authorize`,
      form: true,
      followRedirect: false,
      qs,
    }).then((responseWithToken) => {
      const redirectUrl = responseWithToken.redirectedToUrl;
      cy.log('responseWithToken:', responseWithToken);
      cy.log('redirectUrl:', redirectUrl);

      cy.request({
        method: 'GET',
        url: redirectUrl,
        followRedirect: false,
      });

      cy.visit(url);
    });
  });
});

@onuralp
Copy link

onuralp commented May 23, 2022

Okta supports hosting a sign-in page under your own domain with Embedded Okta Sign-In Widget. In this way, since you use your domain, cypress work seamlessly.

Okta provides a sign-in page, available at your organization's URL, which allows the user to complete the entire authorization flow, start an SSO (Single Sign-On) session, and set the Okta session cookie in the web browser. You can customize this page with a background image and logo. By default, signing in on this page redirects the user to the Okta user dashboard.

See the GitHub repository for the implementation

PS: Use the embed link, so the login process ends with the application you want to test instead of Okta dashboard.

Edit:
Important update: Cypress has introduced cy.origin() command with version 9.6.0 that allows you to visit multiple origins in a single test. See here for details.

@DHenry7471
Copy link

Hey I'm getting this error when running tests/
cy.request() requires a url. You did not provide a url

Should optionsSessionToken have a url in it to provide?

@piyush12
Copy link

piyush12 commented Jun 22, 2023

@iamskok hi, is there any example to test if there is MFA enable in okta, In my app we have enabled the okta sms MFA.

@mmarteli77
Copy link

Hi, i´m newbie in cypress. how to implement Okta DSSO (Desktop single sign-on) authentication using cypress? with DSSO there is no prompt page to enter user/password for authentication, seems that okta does the authentication in the background when i login into the computer. Since cypress use its own browser to run the automation when it hits the app URL i´m getting an error message saying the i´m not allowed to processed since i´m not authenticated. Please help.
Thanks in advance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment