157

如何使用 passport.js 通过 RESTful API 而不是通过 Web 界面处理身份验证(例如本地和 Facebook)?

具体问题是处理将数据从回调传递到 RESTful 响应 (JSON) 与使用典型 res.send({ data: req.data }),设置重定向到 Facebook 的初始 /login 端点(/login 不能通过 AJAX 访问,因为它不是 JSON 响应 - 它是通过回调重定向到 Facebook)。

我找到了https://github.com/halrobertson/test-restify-passport-facebook,但我无法理解它。

此外,passport.js 如何存储身份验证凭据?服务器(或者它是服务?)由 MongoDB 支持,我希望凭据(登录和 pw 的盐渍哈希)存储在那里,但我不知道 passport.js 是否具有这种类型的功能。

4

3 回答 3

316

这里有很多问题,似乎即使这些问题是在 Node 和 passport.js 的上下文中提出的,但真正的问题更多的是关于工作流,而不是如何使用特定技术来做到这一点。

让我们使用@Keith 示例设置,稍作修改以增加安全性:

  • Web 服务器https://example.com为单页 Javascript 客户端应用程序提供服务
  • RESTful Web 服务https://example.com/api为富客户端应用程序提供服务器支持
  • 在 Node 和 passport.js 中实现的服务器。
  • 服务器有一个带有“用户”表的数据库(任何类型)。
  • 提供用户名/密码和 Facebook Connect 作为身份验证选项
  • 富客户端将 REST 请求放入https://example.com/api
  • 可能有其他客户端(例如电话应用程序)使用 Web 服务,https://example.com/api但不了解 Web 服务器https://example.com

请注意,我使用的是安全 HTTP。在我看来,这对于任何公开可用的服务都是必须的,因为密码和授权令牌等敏感信息正在客户端和服务器之间传递。

用户名/密码认证

让我们先看看普通的旧身份验证是如何工作的。

  • 用户连接到https://example.com
  • 服务器提供一个丰富的 Javascript 应用程序来呈现初始页面。Somehwe 在页面中有一个登录表单。
  • 由于用户没有登录,这个单页应用程序的许多部分都没有填充数据。所有这些部分都有一个“登录”事件的事件侦听器。所有这些都是客户端的东西,服务器不知道这些事件。
  • 用户输入他/她的登录名和密码并点击提交按钮,这会触发 Javascript 处理程序在客户端变量中记录用户名和密码。然后这个处理程序触发“登录”事件。同样,这都是客户端操作,凭据尚未发送到服务器
  • 调用“登录”事件的侦听器。现在,它们中的每一个都需要向 RESTful API 发送一个或多个请求,https://example.com/api以获取要在页面上呈现的用户特定数据。他们发送到 Web 服务的每个请求都将包含用户名和密码,可能采用 HTTP基本身份验证的形式,因为不允许使用 RESTful 的服务从一个请求到下一个请求维护客户端状态。由于 Web 服务在安全 HTTP 上,因此密码在传输过程中被安全加密。
  • Web 服务https://example.com/api接收到一堆单独的请求,每个请求都带有身份验证信息。根据用户数据库检查每个请求中的用户名和密码,如果发现正确,则执行请求的函数并将数据以 JSON 格式返回给客户端。如果用户名和密码不匹配,则会以 401 HTTP 错误代码的形式向客户端发送错误。
  • 您可以在 RESTful 服务中使用“get_access_token”函数,而不是强制客户端在每个请求中发送用户名和密码,该函数获取用户名和密码并使用令牌进行响应,令牌是某种唯一且具有一定期限的加密哈希与之相关的日期。这些令牌与每个用户一起存储在数据库中。然后客户端在后续请求中发送访问令牌。然后将根据数据库而不是用户名和密码验证访问令牌。
  • 手机应用程序等非浏览器客户端应用程序与上述相同,它们要求用户输入他/她的凭据,然后将它们(或从它们生成的访问令牌)与每个请求一起发送到 Web 服务。

此示例的重要要点是RESTful Web 服务需要对每个请求进行身份验证

在这种情况下,附加的安全层将在用户身份验证之外添加客户端应用程序授权。例如,如果您的 Web 客户端、iOS 和 Android 应用程序都使用 Web 服务,您可能希望服务器知道给定请求的客户端是这三个客户端中的哪一个,而不管经过身份验证的用户是谁。这可以使您的 Web 服务将某些功能限制为特定客户端。为此,您可以使用 API 密钥和秘密,请参阅此答案以获取一些想法。

脸书认证

上述工作流程不适用于 Facebook 连接,因为通过 Facebook 登录有第三方,即 Facebook 本身。登录过程要求用户被重定向到 Facebook 的网站,在该网站上输入的凭据不受我们的控制。

所以让我们看看事情是如何变化的:

  • 用户连接到https://example.com
  • 服务器提供一个丰富的 Javascript 应用程序来呈现初始页面。页面中有一个登录表单,其中包含“使用 Facebook 登录”按钮。
  • 用户单击“使用 Facebook 登录”按钮,这只是一个重定向到 (例如) 的链接https://example.com/auth/facebook
  • https://example.com/auth/facebook路线由 passport.js 处理(请参阅文档
  • 用户看到的只是页面发生了变化,现在他们位于 Facebook 托管页面中,他们需要登录并授权我们的 Web 应用程序。这完全不在我们的控制范围内。
  • 用户登录 Facebook 并授予我们应用程序的权限,因此 Facebook 现在重定向回我们在 passport.js 设置中配置的回调 URL,按照文档中的示例是https://example.com/auth/facebook/callback
  • 路由的 passport.js 处理程序https://example.com/auth/facebook/callback将调用回调函数,该函数从 Facebook 接收 Facebook 访问令牌和一些用户信息,包括用户的电子邮件地址。
  • 使用电子邮件,我们可以在我们的数据库中找到用户,并用它存储 Facebook 访问令牌。
  • 您在 Facebook 回调中做的最后一件事是重定向回富客户端应用程序,但这次我们需要将用户名和访问令牌传递给客户端,以便客户端可以使用它们。这可以通过多种方式完成。例如,可以通过服务器端模板引擎将 Javascript 变量添加到页面中,或者可以使用此信息返回 cookie。(感谢@RyanKimber 指出在 URL 中传递此数据的安全问题,正如我最初建议的那样)。
  • 所以现在我们再次启动单页应用程序,但客户端拥有用户名和访问令牌。
  • 客户端应用程序可以立即触发“登录”事件,并让应用程序的不同部分从 Web 服务请求他们需要的信息。
  • 发送到的所有请求都https://example.com/api将包括用于身份验证的 Facebook 访问令牌,或者通过 REST API 中的“get_access_token”函数从 Facebook 令牌生成的应用程序自己的访问令牌。
  • 非浏览器应用程序在这里有点困难,因为 OAuth 需要 Web 浏览器才能登录。要从手机或桌面应用程序登录,您需要启动浏览器来重定向到 Facebook,更糟糕的是,您浏览器需要一种方法通过某种机制将 Facebook 访问令牌传递回应用程序。

我希望这能回答大部分问题。当然,您可以将 Facebook 替换为 Twitter、Google 或任何其他基于 OAuth 的身份验证服务。

我很想知道是否有人有更简单的方法来处理这个问题。

于 2013-05-27T22:55:35.540 回答
11

我非常感谢@Miguel 对每种情况的完整流程的解释,但我想在 Facebook 身份验证部分添加一些内容。

Facebook 提供了一个Javascript SDK,您可以使用它直接在客户端获取访问令牌,然后将其传递到服务器并用于进一步从 Facebook 拉取所有用户信息。所以你基本上不需要任何重定向。

此外,您还可以为移动应用程序使用相同的 API 端点。只需使用 Facebook 的 Android / iOS SDK,在客户端获取 Facebook access_token 并将其传递给服务器。

关于所解释的无状态性质,当 get_access_token 用于生成令牌并传递给客户端时,该令牌也存储在服务器上。所以它和会话令牌一样好,我相信这使它有状态?

只是我的2美分..

于 2013-06-06T09:28:54.513 回答
3

这是我发现的一篇很棒的文章,可以帮助您进行身份验证:

  • Facebook
  • 推特
  • 谷歌
  • 本地认证

简易节点身份验证:设置和本地

于 2014-04-22T07:55:55.447 回答