【问题标题】:Auth0 authentication with Cypress使用 Cypress 进行 Auth0 身份验证
【发布时间】:2021-07-17 05:21:10
【问题描述】:

我正在尝试为赛普拉斯创建登录命令,并注意到他们关于如何执行此操作的博客与 Auth0 React SDK 的预期值不匹配。看来他们已经使用自定义快速应用程序来处理登录而不是使用 SDK 来处理此问题(根据 offical Auth0 documentation)。

Cypress official documentation 生成一个本地存储键值对,如下所示。

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

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

然而,由 Auth0 React SDK 创建的那个会产生类似于:

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))

我能够让https://${auth)_domain}/oauth/token 请求正常工作,但是我无法弄清楚如何从响应中获取数据以使其适合 Auth0 react SDK 想要的数据结构。

有人在这方面取得了成功吗?


经过一番探索,我从/oauth/token 得到的响应似乎不包含 Auth0 React SDK 在登录时输出的值的所有字段。

我还注意到Auth0 has a guide 关于如何与赛普拉斯集成,但它不使用此 SDK,而是使用 SPA SDK。该指南还使用了自定义登录表单,我使用的是 LockUI。

需要注意的一点是,我没有使用后端进行身份验证(就像在大多数示例中一样)。我按照官方推荐使用loginWithRedirect登录。

【问题讨论】:

  • 本指南中使用的 SKD 与推荐的 React SDK 不同。它使用了诸如checkSession() 之类的 API,这些 API 在官方 React SDK 中没有公开。
  • 不用担心 checkSession。您不必显式调用它。 Auth0 React SDK 只是 atuh0 spa sdk 的包装器,因此它一直隐式调用 checkSession(例如,当您创建客户端时)。当您使用令牌重定向到回调时,如果您在 Auth0 React SDK 中正确设置了所有内容,您的 sdk 将设置所有必需的 cookie 和存储属性。
  • 您还可以使用 lockUI 完成整个 Lock 工作流程,请参见此处的示例 github.com/auth0/auth0-spa-js/blob/…

标签: javascript reactjs cypress auth0


【解决方案1】:

经过 Auth0 团队的一些调查和帮助,我成功地完成了这项工作。

这是我使用的代码:

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

您必须确保您传入的配置与您在 Auth0 Provider 中使用的配置完全相同相同。

有一次让我感到困惑的是,我也在使用刷新令牌。如果是这种情况,请确保将 offline_access 添加到您的范围内。

我有一个公共存储库来下载一个有效的解决方案 - https://github.com/charklewis/auth0-cypress

【讨论】:

  • 在 2021 年 11 月这仍然对您有效。我尝试了这段代码,但它对我不起作用。我确定这是我错过的东西。有什么建议吗?
  • 根据这个链接community.auth0.com/t/oauth-token-not-returning-id-token/8303你需要在范围顺序中添加openid访问才能获取id_token。否则decodedToken会出错。
  • 一般来说,如果它不起作用,那么您需要使用范围参数来确保它设置正确。
【解决方案2】:

Cypress Real World App 中有一个示例,这是一个支付应用程序,用于演示赛普拉斯测试方法、模式和工作流程的实际使用情况,此外还有一个 Auth0 Authentication Testing Strategies Guide,它详细说明了 Auth0 应用程序和 @ 987654323@.

【讨论】:

  • cypress 真实世界的应用程序非常好,它是使用 Cypress 进行 Auth0 的一个很好的资源我不明白的是在整个代码中使用 if (window.cypress) 以换出使用 id_token 和对 Auth0 React 钩子的实际调用调用 getTokenSilently。上面的另一个答案提供了一种模拟存储在本地存储中的确切内容的方法,这似乎对我有用。基本上,我试图了解是否或为什么需要使用 if (window.cypress) 调整我的代码以使其工作?
【解决方案3】:

对于那些将来遇到这种情况的人。我们创建了一种使用 Cypress 测试 Auth0 的替代方法,它不需要在实际应用程序中更改代码。

我们使用的方法是运行一个本地服务,该服务公开与 Auth0 相同的 API。我们将此服务打包为 NPM 包。您可以在我们的博文https://frontside.com/blog/2022-01-13-auth0-simulator/

中了解它

这是你的测试最终的样子,

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: 'bob@gmail.com'
      })
      .login()
      .visit('/')
      .contains('Log out')
      .logout();
  });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-15
    • 2015-06-29
    • 2016-04-02
    • 2019-06-29
    • 2018-06-01
    • 2015-04-10
    • 2016-12-23
    相关资源
    最近更新 更多