【问题标题】:How to test single page application with Cypress and Auth0如何使用 Cypress 和 Auth0 测试单页应用程序
【发布时间】:2020-04-27 02:28:05
【问题描述】:

我有一个隐藏在 Auth0 锁后面的单页应用程序,使用 @auth0/auth0-spa-js。我想使用 Cypress 进行测试,所以我决定关注官方 Auth0 blog post,以及 Johnny Reilly blog post

我能够使用建议的请求从 auth0 成功检索有效的 JWT 令牌。我不知道该怎么处理它:(

我面临的问题是上述两种方法都依赖应用程序在本地存储 JWT 令牌(在 cookie 或本地存储中)。但是,@auth0/auth0-spa-js 使用了不同的方法,我假设所有相关的 cookie/localstorage 都存储在 auth0 域中。

你有什么想法,如果有办法绕过它?

在 2018 年 7 月报告了类似的问题 here,但并未真正提供任何解决方案

【问题讨论】:

  • 我通过使用基于表单的身份验证运行它来解决测试 oauth2 身份验证的 Spring Boot/安全应用程序的问题 - 使 cypress 的使用非常容易,从 json 固定文件中读取凭据。您能否在测试期间使用不同的身份验证机制?
  • 我认为这意味着您必须在 Auth0 生成的登录表单中输入电子邮件和地址。我想我可以做到这一点,但是我的测试实际上是在测试 Auth0 表单,这是我无法控制的。我宁愿生成一个令牌,然后只是指示 auth0 以某种方式使用它
  • 不,这意味着我在运行我的功能 ui 测试时使用基于普通用户名/密码表单的安全性来回避将 oauth 与 cypress 集成的问题。
  • 哦,所以您有两种身份验证方式。一个为所有用户提供 auth0,然后为 e2e 单独“隐藏”一个?这可能不适用于我的用例,因为 JWT 稍后用于授权属于不同服务的单独 REST 调用
  • 是的,但基于表单的方式仅用于测试。以后的 REST 调用能否在赛普拉斯中被存根?意识到这可能会损害测试的集成方面。无论如何,我想你明白我的意思,当遇到问题时,躲避它..

标签: auth0 cypress auth0-lock


【解决方案1】:

如果您对赛普拉斯的 Google 登录有问题,请查看插件: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
        })
      }
    })
  });

【讨论】:

    【解决方案2】:

    虽然不建议使用 UI 登录,但我在所有测试之前自己执行一次,然后使用静默身份验证进行测试:-cy.visit("/")静默身份验证并允许访问应用程序。

    集成/app.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 */
    

    support/commands.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 = {};
    });
    

    【讨论】:

      【解决方案3】:

      我在@auth0/auth0-spa-js github 上发现了一个已解决的问题。 cwmrowe 建议的 approach 似乎有效

      解决方案是使用 e2e 测试端生成的令牌来模拟 oauth/token 端点的响应。

      这种方法似乎对我们有用

      我正在复制cwmrowe 提供的示例代码

      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>;
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-28
        • 1970-01-01
        • 2019-07-06
        • 2021-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多