password.js RESTful身份验证


155

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

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

我已经找到https://github.com/halrobertson/test-restify-passport-facebook,但是我很难理解它。

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


既然你是新的节点,启动容易,并检查了示例应用程序passport-facebook。完成工作后,下一步就是开始了解Passport的工作方式以及其存储凭据的方式。将其连接到Restify(请参阅此处以获取您提到的更新版本)将是最后一步之一(或者您可以在Express中实现REST接口)。
robertklep

Answers:


312

这里有很多问题,即使问题是在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应用程序。页面中有一个登录表单。
  • 由于用户未登录,此单页面应用程序的许多部分尚未填充数据。所有这些部分在“ login”事件上都有一个事件侦听器。所有这些都是客户端的东西,服务器不知道这些事件。
  • 用户输入他/她的登录名和密码,然后单击“提交”按钮,这将触发Javascript处理程序将用户名和密码记录在客户端变量中。然后,此处理程序触发“登录”事件。同样,这是所有客户端操作,凭据尚未发送到服务器
  • 调用“登录”事件的侦听器。现在,每个这些都需要向RESTful API发送一个或多个请求,https://example.com/api以获取要呈现在页面上的用户特定数据。他们发送到Web服务的每个请求都将包含用户名和密码,可能采用HTTP Basic的形式身份验证,因为不允许使用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中Facebook的令牌通过“ get_access_token”函数从应用程序自身生成的访问令牌。
  • 非浏览器应用程序在这里要困难一些,因为OAuth需要使用网络浏览器登录。要从手机或桌面应用程序登录,您将需要启动浏览器以重定向到Facebook,更糟糕的是,您需要一种让浏览器通过某种机制将Facebook访问令牌传递回应用程序的方法。

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

我很想知道是否有人可以使用更简单的方法来处理此问题。


5
感谢您的详细回复。只是一个问题:您说了Every single request they send to the web service will include the username and password,但是您说了you can have a "get_access_token" function in your RESTful service。说REST需要是无状态的似乎是矛盾的,但是在服务器端存储访问令牌是可以的,因为存储访问令牌的行为意味着服务器现在是有状态的。我希望对此有任何澄清或理由。谢谢!:)
ryanrhee 2013年

6
当我想到无状态需求时,我会考虑与客户进行特定会话的状态。我看不到在数据库中存储与用户关联的数据(例如密码或访问令牌)为“状态”,至少不是“会话状态”。您的服务器显然需要知道用户名和密码,但这是与会话无关的应用程序数据。就是说,许多人在所谓的RESTful服务中使用cookie,因此,您要遵守REST规范的严格程度实际上取决于每个实现者。
米格尔(Miguel),

1
@Dexter:在传统的登录情况下,用户在表单中输入用户名和密码,然后单击“提交”按钮,此信息就会发布到Web服务器。在这种情况下,不会发生这种情况,用户填写表单,然后单击“提交Javascript处理程序”(“提交”按钮中的onClick事件),它将捕获数据并将其保存在客户端上下文中。我没有准备显示的示例,但请在我的博客上关注本教程的第二部分,其中将演示如何完成此操作:blog.miguelgrinberg.com/post/…–
Miguel

2
这是一个经过深思熟虑的文章,但其中包含一个重要的疏忽或错误。在处理Facebook登录名(或Github,twitter等)时,最好将令牌以cookie的形式传递回客户端,然后在发现cookie后在客户端将其删除。将令牌作为URL字符串的一部分传递会将此URL添加到浏览器历史记录中,并且(如果处理不当)可能导致浏览器请求此URL。要么使令牌可见。复制URL的任何人都可以在您的应用程序中欺骗该用户。
Ryan Kimber 2014年

1
@Nathan:基于https的基本身份验证是安全的,所以是的,这是可以接受的机制。
Miguel

11

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

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

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

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

就是我的2美分


1
这非常重要,因为通过这种方式,您可以使用facebook进行仅一页的ajax身份验证。令牌作为会话身份验证是绝对安全的,唯一的区别是令牌可以传递到另一个域,并且该会话仅在一个特定域中使用。使用expressjs和Passport时,您可以创建一个保存状态的api,并使用会话身份验证。
jperelli 2014年

如果您想对用户进行身份验证以对Facebook执行操作,则javascript api很棒,但是如果要根据我的判断对服务器/数据库进行验证,则javascript API本身就没有用。
James Westgate 2014年

4
您也可以使用上面Miguel所描述的方法,但是当在auth回调中重定向客户端时,然后将您自己的JWT令牌作为cookie发出。这样可以兼得两全其美-您的单页面应用程序可以只关注一种身份验证(JWT),同时保持相同级别的安全性并提供支持任何社交登录的灵活性,而不必使用每个社交网络(Facebook,Twitter,LinkedIn,Google等)的特定JavaScript API。它还使您可以维护对用户名/密码和REST访问的AJAX样式支持。
Ryan Kimber

Facebook Javascript SDK当前不适用于Chrome iOS。也许对某些人来说是个问题。
demisx

@RyanKimber您可以写一个小的git repo或类似的东西作为示例,但是我完全陷入了困境
SimonDragsbæk16年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.