2
votes

Comment tester une application monopage avec Cypress et Auth0

J'ai une application d'une seule page cachée derrière le verrou Auth0, en utilisant @ auth0 / auth0-spa -js . Je voudrais le tester à l'aide de Cypress, j'ai donc décidé de suivre la Auth0 article de blog , ainsi que Johnny Reilly article de blog .

Je parviens à récupérer un jeton JWT valide à partir de auth0 à l'aide de la requête suggérée. Je ne sais pas quoi en faire :(

Le problème auquel je suis confronté est que les deux approches ci-dessus reposent sur l'application pour stocker le jeton JWT localement (dans un cookie ou un stockage local). Le @ auth0 / auth0-spa-js utilise cependant une approche différente, et je suppose que tous les cookies / stockage local pertinents sont stockés sur les domaines auth0.

Avez-vous une idée, s'il existe un moyen de le contourner?

Un problème similaire a été signalé ici soulevé en juillet 2018, n'apportant aucune solution


5 commentaires

J'ai géré le problème de tester une application de démarrage / sécurité à ressort authentifiée oauth2 en l'exécutant à l'aide de l'authentification basée sur un formulaire - l'utilisation de cyprès a été très facile, en lisant les informations d'identification à partir d'un fichier de fixation json. Pouvez-vous utiliser un mécanisme d'authentification différent pendant les tests?


Je suppose que cela signifiait que vous deviez entrer l'e-mail et l'adresse dans le formulaire de connexion généré par Auth0. Je suppose que je pourrais le faire, mais mes tests testeraient en fait le formulaire Auth0, qui est hors de mon contrôle. Je préférerais générer un jeton, puis demander simplement à auth0 de l'utiliser d'une manière ou d'une autre


non, cela signifiait que j'avais contourné le problème de l'intégration d'oauth avec cypress en utilisant la sécurité basée sur un nom d'utilisateur / mot de passe vanilla lors de l'exécution de mes tests d'interface utilisateur fonctionnelle.


oh, vous avez donc 2 façons de vous authentifier. Un avec auth0 pour tous les utilisateurs, puis un séparé "caché" pour e2e? Cela ne fonctionnera probablement pas pour mon cas d'utilisation, car le JWT est utilisé plus tard pour autoriser un appel REST distinct, appartenant à un service différent


oui, mais la méthode basée sur le formulaire n'est utilisée que dans les tests. Les derniers appels REST pourraient-ils être stubbés dans Cypress? Sachez que cela pourrait compromettre l'aspect d'intégration des tests. Quoi qu'il en soit, je pense que vous comprenez mon point, face à un problème, esquivez-le.


3 Réponses :


3
votes

J'ai trouvé un problème résolu sur @ auth0 / auth0-spa-js github. L ' approche suggérée par cwmrowe semble fonctionner

La solution est de se moquer de la réponse du point de terminaison oauth / token avec le jeton généré côté test e2e.

L'approche semble fonctionner pour nous

Je copie l'exemple de code cwmrowe a fourni

Cypress.Commands.add(
  'login',
  (username, password, appState = { target: '/' }) => {
    cy.log(`Logging in as ${username}`);
    const options = {
      method: 'POST',
      url: Cypress.env('Auth0TokenUrl'),
      body: {
        grant_type: 'password',
        username,
        password,
        audience: Cypress.env('Auth0Audience'),
        scope: 'openid profile email',
        client_id: Cypress.env('Auth0ClientId'),
        client_secret: Cypress.env('Auth0ClientSecret')
      }
    };
    cy.request(options).then(({ body }) => {
      const { access_token, expires_in, id_token } = body;

      cy.server();

      // intercept Auth0 request for token and return what we have
      cy.route({
        url: 'oauth/token',
        method: 'POST',
        response: {
          access_token,
          expires_in,
          id_token,
          token_type: 'Bearer'
        }
      });

      // Auth0 SPA SDK will check for value in cookie to get appState
      // and validate nonce (which has been removed for simplicity)
      const stateId = 'test';
      const encodedAppState = encodeURI(JSON.stringify(appState));
      cy.setCookie(
        `a0.spajs.txs.${stateId}`,
        `{%22appState%22:${encodedAppState}%2C%22scope%22:%22openid%20profile%20email%22%2C%22audience%22:%22default%22}`
      );

      const callbackUrl = `/auth/callback?code=test-code&state=${stateId}`;
      return cy.visit(callbackUrl);
    });
  }
);

declare namespace Cypress {
  interface Chainable<Subject> {
    login(
      username: string,
      password: string,
      appState?: any
    ): Chainable<Subject>;
  }
}


0 commentaires

0
votes

Bien qu'il ne soit pas recommandé d'utiliser l'interface utilisateur pour me connecter, je le fais moi-même une fois avant tous les tests, puis j'utilise l'authentification silencieuse pour les tests: - cy.visit ("/") silent auths et autorise l'accès à l'application.

integration / app.js

/**
 * Auth0 login
 * https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888
 *
 * Allows silent auth login between tests
 */
let LOCAL_STORAGE_MEMORY = {};

Cypress.Commands.add("saveLocalStorage", () => {
  Object.keys(localStorage).forEach(key => {
    LOCAL_STORAGE_MEMORY[key] = localStorage[key];
  });
});

Cypress.Commands.add("restoreLocalStorage", () => {
  Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
    localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
  });
});

Cypress.Commands.add("clearLocalStorage", () => {
  LOCAL_STORAGE_MEMORY = {};
});

support / commands.js

describe("App", () => {
  before(() => {
    Cypress.config("baseUrl", "http://localhost:3000");
    cy.login();
  });

  /** Uses silent auth for successive tests */
  beforeEach(() => {
    cy.restoreLocalStorage();
  });

  afterEach(() => {
    cy.saveLocalStorage();
  });

  /** tests */


0 commentaires

0
votes

Pour ceux qui ont des problèmes avec Google Sign in for Cypress, regardez le plugin: https: //github.com/lirantal/cypress-social-logins/

  it('Login through Google', () => {
    const username = Cypress.env('googleSocialLoginUsername')
    const password = Cypress.env('googleSocialLoginPassword')
    const loginUrl = Cypress.env('loginUrl')
    const cookieName = Cypress.env('cookieName')
    const socialLoginOptions = {
      username,
      password,
      loginUrl,
      headless: false,
      isPopup: true,
      logs: false,
      loginSelector: 'a[href="/auth/auth0/google-oauth2"]',
      postLoginSelector: '.account-panel'
    }

    return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies}) => {
      cy.clearCookies()

      const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
      if (cookie) {
        cy.setCookie(cookie.name, cookie.value, {
          domain: cookie.domain,
          expiry: cookie.expires,
          httpOnly: cookie.httpOnly,
          path: cookie.path,
          secure: cookie.secure
        })

        Cypress.Cookies.defaults({
          whitelist: cookieName
        })
      }
    })
  });


0 commentaires