passport.js RESTful auth

rya*_*hee 155 authentication rest node.js restify passport.js

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

特别关注的是处理从回调到RESTful响应(JSON)的数据传递与使用典型的res.send({data:req.data}),设置重定向到Facebook的初始/登录端点(/登录不能是通过AJAX访问,因为它不是JSON响应 - 它是一个带有回调的Facebook重定向.

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

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

Mig*_*uel 311

这里提出了许多问题,似乎即使问题是在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应用程序,用于呈现初始页面.在页面中有一个登录表单.
  • 由于用户未登录,此单页应用程序的许多部分尚未填充数据.所有这些部分都在"登录"事件中具有事件侦听器.所有这些都是客户端的东西,服务器不知道这些事件.
  • 用户输入他/她的登录名和密码并点击提交按钮,这会触发Javascript处理程序以在客户端变量中记录用户名和密码.然后此处理程序触发"登录"事件.同样,这是所有客户端操作,凭据尚未发送到服务器.
  • 调用"login"事件的侦听器.其中每个都需要向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服务的Web客户端,iOS和Android应用程序,您可能希望服务器知道给定请求的三个客户端中的哪一个,而不管经过身份验证的用户是谁.这可以使您的Web服务将某些功能限制为特定客户端.为此你可以使用API​​密钥和秘密,请参阅这个答案,了解一些想法.

Facebook身份验证

上面的工作流程不适用于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
  • https://example.com/auth/facebook/callback路由的passport.js处理程序将调用回调函数,该函数接收来自Facebook的Facebook访问令牌和一些用户信息,包括用户的电子邮件地址.
  • 通过电子邮件,我们可以在我们的数据库中找到用户并使用它存储Facebook访问令牌.
  • 你在Facebook回调中做的最后一件事是重定向回富客户端应用程序,但这次我们需要将用户名和访问令牌传递给客户端,以便它可以使用它们.这可以通过多种方式完成.例如,可以通过服务器端模板引擎将Javascript变量添加到页面中,否则可以使用此信息返回cookie.(感谢@RyanKimber指出了在URL中传递此数据的安全问题,正如我最初建议的那样).
  • 所以现在我们再次启动单页面应用程序,但客户端具有用户名和访问令牌.
  • 客户端应用程序可以立即触发"登录"事件,并让应用程序的不同部分从Web服务请求所需的信息.
  • 发送到的所有请求https://example.com/api将包括用于身份验证的Facebook访问令牌,或者通过REST API中的"get_access_token"函数从Facebook的令牌生成的应用程序自己的访问令牌.
  • 非浏览器应用程序在这里有点困难,因为OAuth需要Web浏览器才能登录.要从手机或桌面应用程序登录,您需要启动浏览器才能重定向到Facebook,更糟糕的是,你需要一种方法让浏览器通过某种机制将Facebook访问令牌传递回应用程序.

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

我有兴趣知道是否有人有更简单的方法来解决这个问题.

  • 当我考虑无状态要求时,我会考虑与客户进行特定会话的状态.我没有看到将与用户相关联的数据存储在数据库中,如密码或访问令牌为"状态",至少不是"会话状态".您的服务器显然需要知道用户是谁以及他们的密码,但这是与会话无关的应用程序数据.也就是说,很多人在所谓的RESTful服务中使用cookie,因此您希望遵守REST规范的严格程度取决于每个实现者. (6认同)
  • 感谢您的详细回复.只有一个问题:您说"他们发送给Web服务的每个请求都将包含用户名和密码",但您说"您可以在RESTful服务中使用"get_access_token"函数.说REST需要无状态似乎是矛盾的,但存储访问令牌服务器端是可以的,因为存储访问令牌的行为意味着服务器现在是有状态的.我将不胜感激任何关于此的澄清或理由.谢谢!:) (5认同)
  • 这是一个经过深思熟虑的写作,但包含一个重要的疏忽或错误.在处理Facebook登录(或Github,Twitter等)时,最好将令牌传递回cookie中的客户端,然后在发现后删除客户端的cookie.将令牌作为URL字符串的一部分传递将此URL添加到浏览器历史记录中(如果处理不当)可能导致浏览器请求此URL.使令牌可见.然后,复制URL的任何人都可能在您的应用程序中欺骗此用户. (2认同)

Mad*_*hur 11

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

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

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

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

只需我2美分..

  • 您还可以使用上面Miguel描述的方法,但在auth回调中重定向客户端时,可以将自己的JWT令牌作为cookie发布.这样两全其美的 - 你的单页的应用程序,可以关注只有一个(JWT)类型的认证,同时保持相同的安全级别,并提供了灵活性,以支持任何社会登录,而无需使用每个社交网络(Facebook,Twitter,LinkedIn,Google等)的特定JavaScript API.它还允许您维护AJAX样式的用户名/密码和REST访问支持. (4认同)