3

我正在尝试将 Auth0 身份验证添加到我的单页应用程序中。我的应用程序在一个域下运行,例如 app.mycompany.com,而此应用程序使用的 api 在另一个域下运行,例如 api.mycompany.com。

我知道这个线程:

单页应用程序 (SPA) 的单点登录 (SSO) 解决方案/架构

以及此处链接的 auth0 文章和 github 存储库。但我感觉我的场景稍微简单一些,因为我不一定希望在几个不同的单页应用程序之间进行单点登录。首先,我只想要 API 和应用程序之间的分离。

这是我已经尝试过的:

我已经从文章React Login With Auth0 开始并下载了启动项目。我肯定可以毫无问题地登录,它会在我的 localStorage 中留下一个 id_token,其中包含由 Auth0 发布的 JWS。

我也可以直接登录 api.mycompany.com(我的 FeathersJS API 应用程序),我可以看到在 OAuth 重定向过程中,id_token 令牌神奇地转换为我的 Feathers 应用程序发出的包含内部 ID 的 feathers-jwt 令牌与 auth0-ID 匹配的用户对象。我还实现了用于从 Auth0-ID 映射到我的内部 ID 的逻辑。此外,我所有的 Feathers 钩子(例如验证令牌和用户数量)都在工作。

我无法弄清楚的是如何使用 localStorage 中的 Auth0 令牌更改在 app.mycompany.com 下运行的 react-application,以便此令牌由 api.mycompany.com 转换为 feathers-jwt 令牌,在这样的这样所有后续的 API 调用都会自动包含 feathers-jwt 令牌,以便 API 可以验证用户并返回正确的数据。

任何有关如何进行的建议将不胜感激。

更多背景细节:

  • 该 api 建立在 node.js 和 featherjs 上(基本上是 Express 的扩展)
  • 单页应用程序基于 ReactJS 构建,由一个简单的 Express 服务器提供服务,但它可以由任何可以通过 http 提供静态文件的服务器提供服务。单页应用程序向 api 发出 http 请求以读取数据并执行操作。
  • 该 api 具有以下代码行来处理身份验证:

    const authentication = require('feathers-authentication');
    const Auth0Strategy = require('passport-auth0').Strategy;
    app.configure(authentication({
        local:false,
        token: {
          secret: 'mysecret',
          payload: ['email', 'auth0Nickname'],
          issuer: 'mycompany'
        },
        idField: 'id',
        shouldSetupSuccessRoute: false,
        auth0: {
          strategy: Auth0Strategy,
          domain: 'mycompany.eu.auth0.com',
          'clientID': 'xxx',
          'clientSecret': 'yyy'
        }
    }));
    
4

2 回答 2

3

我遇到了与您完全相同的问题,我想从单页应用程序中对用户进行身份验证,调用位于另一台服务器上的 API。

官方的auth0 示例是一个经典的 Express Web 应用程序,它执行身份验证并呈现 html 页面,但它不是连接到托管在其他域上的 API 的 SPA。

让我们分解此示例中用户进行身份验证时发生的情况:

  • 用户发出请求调用/auth/auth0路由
  • 用户被自动重定向到 Auth0 身份验证过程(Auth0 登录表单以选择提供者,然后选择提供者登录屏幕)
  • 用户被重定向到/auth/success路由
  • /auth/success路由重定向到静态 html 页面public/success.html,同时发送一个jwt-token包含用户令牌的 cookie
  • 客户端,public/success.html加载时,Feathers 客户端authenticate()方法从 cookie 中读取令牌并将其保存在本地存储中。

从现在开始,Feathers 客户端将对从本地存储读取 cookie 的用户进行身份验证。

我尝试将这种场景调整为单页应用程序架构,实现以下过程:

  • source在 SPA 中,使用包含 SPA URL的查询字符串参数调用身份验证 API 。例如:http://my-api.com/auth/auth0?source=http://my-spa.com
  • 服务器端,在/auth/auth0路由处理程序中,创建一个 cookie 来存储该 URL
  • 成功登录后,读取sourcecookie 以将用户重定向回 SPA,并在 cookie 中发送 JWT 令牌。

但是最后一步不起作用,因为您无法在给定域(API 服务器域)上设置 cookie 并将用户重定向到另一个域!(更多关于这Stackoverflow 上)

所以实际上我通过以下方式解决了这个问题:

  • 服务器端:使用 URL 哈希将令牌发送回客户端。
  • 客户端:创建一个新的 html 页面,该页面从 URL 哈希中读取令牌

服务器端代码:

// Add a middleware to write in a cookie where the user comes from
// This cookie will be used later to redirect the user to the SPA
app.get('/auth/auth0', (req, res, next) => {
  const { origin } = req.query
  if (origin) {
    res.cookie(WEB_CLIENT_COOKIE, origin)
  } else {
    res.clearCookie(WEB_CLIENT_COOKIE)
  }
  next()
})

// Route called after a successful login
// Redirect the user to the single-page application "forwarding" the auth token
app.get('/auth/success', (req, res) => {
  const origin = req.cookies[WEB_CLIENT_COOKIE]
  if (origin) {
    // if there is a cookie that contains the URL source, redirect the user to this URL
    // and send the user's token in the URL hash
    const token = req.cookies['feathers-jwt']
    const redirectUrl = `${origin}/auth0.html#${token}`
    res.redirect(redirectUrl)
  } else {
    // otherwise send the static page on the same domain.
    res.sendFile(path.resolve(process.cwd(), 'public', 'success.html'))
  }
})

客户端,auth0.htmlSPA 中的页面

在 SPA 中,我创建了一个新的 html 页面,我称之为auth0.html它做 3 件事:

  • 它从哈希中读取令牌
  • 它将它保存在本地存储中(模仿 Feathers 客户端所做的)
  • 它将用户重定向到 SPA 主页index.html

html代码:

<html>
<body>
  <script>
  function init() {
    const token = getToken()
    if (!token) {
      console.error('No auth token found in the URL hash!')
    }
    // Save the token in the local storage
    window.localStorage.setItem('feathers-jwt', token)
    // Redirect to the single-page application
    window.location.href = '/'
  }

  // Read the token from the URL hash
  function getToken() {
    const hash = self.location.hash
    const array = /#(.*)/.exec(hash)
    if (!array) return
    return array[1]
  }

  init()
  </script>
</body>
</html>

现在在 SPA 中,我可以使用 Feathers 客户端,在应用启动时从本地存储中读取令牌。

让我知道这是否有意义,谢谢!

于 2016-11-13T13:16:05.253 回答
2

如果您还没有这样做,您应该按照这篇文章(React Login with Auth0)在您的 React 应用程序上实现身份验证。如果您已经尝试关注它,请用您面临的具体问题更新您的问题。

即使您目前不需要 SSO,但您的应用程序中身份验证的实际实现不会有太大变化。通过使用 Auth0 在您的应用程序中启用 SSO 主要是启用配置开关。

最后,完整参考您的确切场景检查的安全相关方面背后的所有理论:

Auth0 架构场景:SPA + API


更新:

我链接的完整场景也涵盖了最全面的场景,其中 API 被大量客户端应用程序访问,这些客户端应用程序甚至可能由不拥有受保护 API 但想要访问其背后数据的第三方开发。

它通过利用目前仅在美国地区可用的最新功能来做到这一点,并且在非常高的级别上可以描述为作为服务交付的 OAuth 2.0 授权服务器。

您的特定场景更简单,API 和客户端应用程序都在同一个实体的控制之下,因此您有另一种选择。

选项 1 - 仅通过 Auth0美国区域利用 API 授权(目前)

在这种情况下,您的客户端应用程序在身份验证时将收到一个id_token用于了解当前经过身份验证的用户的信息,并且还将收到一个access_token可用于代表经过身份验证的用户调用 API 的信息。

这明确区分了客户端应用程序和 API;用于id_token客户端应用程序使用和access_tokenAPI 使用。

它的好处是授权与身份验证明显分开,您可以通过控制访问令牌中包含的范围来对授权决策进行非常细粒度的控制。

选项 2 - 以相同方式在客户端应用程序和 API 中进行身份验证

您可以分别部署客户端应用程序和 API,但仍从概念角度将它们视为同一个应用程序(您将在 Auth0 中配置一个客户端,代表客户端和 API)。

这样做的好处是,您可以使用id_token在身份验证完成后获得的信息来了解客户端上的用户是谁,以及作为对每个 API 请求进行身份验证的机制。

您必须配置羽毛 API 以验证 Auth0id_token作为访问 API 的接受令牌。这意味着您不会使用任何基于 API 身份验证的功能,也就是说,您只需接受 Auth0 颁发给您的应用程序的令牌作为验证访问的方式。

于 2016-11-09T09:28:01.027 回答