Charklewis
Charklewis

Reputation: 5621

Auth0 authentication with Cypress

I am trying to create a login command for Cypress and noticed their blog on how to do this does not match the expected values for the Auth0 React SDK. It appears they have used a custom express app to handle the login vs using the SDK to handle this (as per the offical Auth0 documentation).

The Cypress official documentation produces a local storage key value pair that looks like the below.

const item = {
        body: {
          decodedToken: {
            claims,
            user: { ... },
            audience,
            client_id,
          },
        },
        expiresAt: exp,
      }

window.localStorage.setItem('auth0Cypress', JSON.stringify(item))

However the one created by the Auth0 React SDK produces something similar to:

const item = {
      body: {
        access_token,
        audience,
        client_id,
        decodedToken: {
          claims,
          user: { ... },
          encoded,
          header
        },
        expires_in,
        id_token,
        scope,
        token_type
      },
      expiresAt: exp
    }

window.localStorage.setItem(`@@auth0spajs@@::${client_id}::${audience}::${scope}`, JSON.stringify(item))

I am able to get the https://${auth)_domain}/oauth/token request working, however am not able to work out how to get the data from the response in a way for it to fit the data structure the Auth0 react SDK wants it in.

Has anyone had any success with this?


After doing some exploring, it appears the response I get back from the /oauth/token does not contain all of the fields that the value the Auth0 React SDK outputs when it signs in.

I have also noticed that Auth0 has a guide on how to integrate with Cypress however it does not use this SDK, instead it uses the SPA SDK. That guide also uses a custom login form, where I am using the LockUI.

One thing to note is that I am not using an backend to authenticate (like in most of the examples). I using the loginWithRedirect to login as per the offical recommendation.

Upvotes: 5

Views: 4719

Answers (3)

Taras Mankovski
Taras Mankovski

Reputation: 1659

For those who come across this in the future. We created an alternative approach of testing Auth0 with Cypress which doesn't require changing code in the actual application.

The approach that we use is to run a local service that exposes the same API as Auth0. We packages this service as an NPM package. You can read about it in our blog post https://frontside.com/blog/2022-01-13-auth0-simulator/

Here is what your test end up looking like,

import auth0Config from '../../cypress.env.json';

describe('log in', () => {
  it('should get token without signing in', () => {
    cy.createSimulation(auth0Config)
      .visit('/')
      .contains('Log out')
      .should('not.exist')
      .given({
        email: '[email protected]'
      })
      .login()
      .visit('/')
      .contains('Log out')
      .logout();
  });
});

Upvotes: 1

Kevin Old
Kevin Old

Reputation: 1029

There is an example in the Cypress Real World App, a payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows in addition to a Auth0 Authentication Testing Strategies Guide which details the changes in to an Auth0 application and the Cypress Real World App.

Upvotes: 0

Charklewis
Charklewis

Reputation: 5621

After a bit of investigating and help from the Auth0 team, I was successful in making this work.

Here is the code I used:

Cypress.Commands.add("login", () => {
  cy.clearLocalStorage();

  const email = "";
  const password = "";
  const client_id = "";
  const client_secret = "";
  const audience = "";
  const scope = "";

  cy.request({
    method: "POST",
    url: "",
    body: {
      grant_type: "password",
      username: email,
      password,
      audience,
      scope,
      client_id,
      client_secret,
    },
  }).then(({ body: { access_token, expires_in, id_token, token_type } }) => {
    cy.window().then((win) => {
      win.localStorage.setItem(
        `@@auth0spajs@@::${client_id}::${audience}::${scope}`,
        JSON.stringify({
          body: {
            client_id,
            access_token,
            id_token,
            scope,
            expires_in,
            token_type,
            decodedToken: {
              user: JSON.parse(
                Buffer.from(id_token.split(".")[1], "base64").toString("ascii")
              ),
            },
            audience,
          },
          expiresAt: Math.floor(Date.now() / 1000) + expires_in,
        })
      );
      cy.reload();
    });
  });
});

You have to make sure that the config you pass in is exactly the same as the config you use in the Auth0 Provider.

Once thing to note that tripped me up, was that I was also using refresh tokens. If this is the case make sure to add offline_access to your scope.

I have a public repo to download a working solution - https://github.com/charklewis/auth0-cypress.

Upvotes: 4

Related Questions