node.js passport.js RESTful 身份验证
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14572600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
passport.js RESTful auth
提问by ryanrhee
How does one handle authentication (local and Facebook, for example) using passport.js, through a RESTful API instead of through a web interface?
如何使用passport.js 通过RESTful API 而不是通过Web 界面处理身份验证(例如本地和Facebook)?
Specific concerns are handling the passing of data from callbacks to a RESTful response (JSON) vs using a typical res.send({ data: req.data }), setting up an initial /login endpoint which redirects to Facebook (/login cannot be accessed via AJAX, because it is not a JSON response - it is a redirect to Facebook with a callback).
具体问题是处理从回调到 RESTful 响应 (JSON) 的数据传递与使用典型的 res.send({ data: req.data }),设置重定向到 Facebook 的初始 /login 端点(/login 不能是通过 AJAX 访问,因为它不是 JSON 响应 - 它是通过回调重定向到 Facebook)。
I've found https://github.com/halrobertson/test-restify-passport-facebook, but I'm having trouble understanding it.
我找到了https://github.com/halrobertson/test-restify-passport-facebook,但我无法理解它。
Furthermore, how does passport.js store the auth credentials? The server (or is it service?) is backed by MongoDB, and I'd expect credentials (login & salted hash of pw) to be stored there, but I don't know if passport.js has this type of capability.
此外,passport.js 如何存储认证凭证?服务器(或者它是服务?)由 MongoDB 支持,我希望凭据(登录和加密的 pw 哈希)存储在那里,但我不知道 Passport.js 是否具有这种类型的功能。
回答by Miguel
There are many questions asked here, and it seems that even though the questions are asked in the context of Node and passport.js the real questions are more about workflow than how to do this with a particular technology.
这里提出了很多问题,似乎即使这些问题是在 Node 和passport.js 的上下文中提出的,但真正的问题更多是关于工作流程,而不是如何使用特定技术来做到这一点。
Let's use @Keith example setup, modified a bit for added security:
让我们使用@Keith 示例设置,稍作修改以增加安全性:
- Web server at
https://example.comserves a single page Javascript client app - RESTful web service at
https://example.com/apiprovides server support to rich client app - Server implemented in Node and passport.js.
- Server has a database (any kind) with a "users" table.
- Username/password and Facebook Connect are offered as authentication options
- Rich client makes REST requests into
https://example.com/api - There may be other clients (phone apps, for example) that use the web service at
https://example.com/apibut do not know about the web server athttps://example.com.
- Web 服务器
https://example.com提供单页 Javascript 客户端应用程序 - RESTful Web 服务 at
https://example.com/api为富客户端应用程序提供服务器支持 - 在 Node 和passport.js 中实现的服务器。
- 服务器有一个带有“用户”表的数据库(任何类型)。
- 提供用户名/密码和 Facebook Connect 作为身份验证选项
- 富客户端将 REST 请求转化为
https://example.com/api - 可能有其他客户端(例如电话应用程序)使用 处的 Web 服务
https://example.com/api但不知道 处的 Web 服务器https://example.com。
Note that I'm using secure HTTP. This is in my opinion a must for any service that is available in the open, since sensitive information like passwords and authorization tokens are passing between client and server.
请注意,我使用的是安全 HTTP。在我看来,这对于任何公开可用的服务都是必须的,因为密码和授权令牌等敏感信息在客户端和服务器之间传递。
Username/password authentication
用户名/密码认证
Let's look at how plain old authentication works first.
让我们先看看普通的旧式身份验证是如何工作的。
- The user connects to
https://example.com - The server serves a rich Javascript application which renders the initial page. Somehwere in the page there is a login form.
- Many of the sections of this single page app haven't been populated with data due to the user not being logged in. All these sections have an event listener on a "login" event. All this is client side stuff, the server does not know of these events.
- User enters his/her login and password and hits the submit button, which triggers a Javascript handler to record the username and password in client side variables. Then this handler triggers the "login" event. Again, this is all client side action, credentials were not sent to the server yet.
- The listeners of the "login" event are invoked. Each of these now needs to send one or more requests to the RESTful API at
https://example.com/apito obtain the user specific data to render on the page. Every single request they send to the web service will include the username and password, possibly in the form of HTTP Basicauthentication, since the service being RESTful isn't allowed to maintain client state from one request to the next. Since the web service is on secure HTTP the password is safely encrypted during transit. - The web service at
https://example.com/apireceives a bunch of individual requests, each with authentication information. The username and password in each request is checked against the user database and if found correct the requested function executes and data is returned to the client in JSON format. If username and password do not match an error is sent to the client in the form of a 401 HTTP error code. - Instead of forcing clients to send username and password with every request you can have a "get_access_token" function in your RESTful service that takes the username and password and responds with a token, which is some sort of cryptographic hash that is unique and has some expiration date associated with it. These tokens are stored in the database with each user. Then the client sends the access token in subsequent requests. The access token will then be validated against the database instead of the username and password.
- Non browser client applications like phone apps do the same as above, they ask user to enter his/her credentials, then send them (or an access token generated from them) with every request to the web service.
- 用户连接到
https://example.com - 服务器提供丰富的 Javascript 应用程序,用于呈现初始页面。页面中的某个地方有一个登录表单。
- 由于用户未登录,这个单页应用程序的许多部分没有填充数据。所有这些部分都有一个“登录”事件的事件侦听器。所有这些都是客户端的东西,服务器不知道这些事件。
- 用户输入他/她的登录名和密码并点击提交按钮,这会触发 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 服务的每个请求一起发送。
The important take away point from this example is that RESTful web services require authentication with every request.
这个例子的重要收获是RESTful Web 服务要求对每个请求进行身份验证。
An additional layer of security in this scenario would add client application authorization in addition to the user authentication. For example, if you have the web client, iOS and Android apps all using the web service you may want the server to know which of the three the client of a given request is, regardless of who the authenticated user is. This can enable your web service to restrict certain functions to specific clients. For this you could use API keys and secrets, see this answerfor some ideas on that.
除了用户身份验证之外,此场景中的附加安全层将添加客户端应用程序授权。例如,如果您有 Web 客户端、iOS 和 Android 应用程序都使用 Web 服务,您可能希望服务器知道给定请求的客户端是三个中的哪一个,而不管经过身份验证的用户是谁。这可以使您的 Web 服务将某些功能限制为特定客户端。为此,您可以使用 API 密钥和秘密,有关这方面的一些想法,请参阅此答案。
Facebook authentication
脸书认证
The workflow above does not work for Facebook connect because the login via Facebook has a third party, Facebook itself. The login procedure requires the user to be redirected to Facebook's website where credentials are entered outside of our control.
上述工作流程不适用于 Facebook 连接,因为通过 Facebook 登录有第三方,即 Facebook 本身。登录程序要求将用户重定向到 Facebook 的网站,在那里输入的凭据不受我们控制。
So let's see how things change:.
那么让我们看看事情是如何变化的:
- The user connects to
https://example.com - The server serves a rich Javascript application which renders the initial page. Somehwere in the page there is a login form that includes a "Login with Facebook" button.
- The user clicks the "Login with Facebook" button, which is just a link that redirects to (for example)
https://example.com/auth/facebook. - The
https://example.com/auth/facebookroute is handled by passport.js (see the documentation) - All the user sees is that the page changes and now they are in a Facebook hosted page where they need to login and authorize our web application. This is completely outside of our control.
- The user logs in to Facebook and gives permission to our application, so Facebook now redirects back to the callback URL that we configured in the passport.js setup, which following the example in the documentationis
https://example.com/auth/facebook/callback - The passport.js handler for the
https://example.com/auth/facebook/callbackroute will invoke the callback function that receives the Facebook access token and some user information from Facebook, including the user's email address. - With the email we can locate the user in our database and store the Facebook access token with it.
- The last thing you do in the Facebook callback is to redirect back to the rich client application, but this time we need to pass the username and the access token to the client so that it can use them. This can be done in a number of ways. For example, Javascript variables can be added to the page through a server-side template engine, or else a cookie can be returned with this information. (thanks to @RyanKimber for pointing out the security issues with passing this data in the URL, as I initially suggested).
- So now we start the single page app one more time, but the client has the username and the access token.
- The client application can trigger the "login" event immediately and let the different parts of the application request the information that they need from the web service.
- All the requests sent to
https://example.com/apiwill include the Facebook access token for authentication, or the application's own access token generated from Facebook's token via a "get_access_token" function in the REST API. - The non-browser apps have it a bit more difficult here, because OAuth requires a web browser for logging in. To login from a phone or desktop app you will need to start a browser to do the redirect to Facebook, and even worse, you need a way for the browser to pass the Facebook access token back to the application via some mechanism.
- 用户连接到
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 需要网络浏览器才能登录。要从手机或桌面应用程序登录,您需要启动浏览器来重定向到 Facebook,更糟糕的是,您需要一种让浏览器通过某种机制将 Facebook 访问令牌传递回应用程序的方法。
I hope this answers most of the questions. Of course you can replace Facebook with Twitter, Google, or any other OAuth based authentication service.
我希望这能回答大部分问题。当然,您可以用 Twitter、Google 或任何其他基于 OAuth 的身份验证服务替换 Facebook。
I'd be interested to know if someone has a simpler way to deal with this.
我很想知道是否有人有更简单的方法来处理这个问题。
回答by Madhur
I greatly appreciate @Miguel's explanation with the complete flow in each cases, but I'd like to add some on the Facebook Authentication part.
我非常感谢@Miguel 对每种情况的完整流程的解释,但我想在 Facebook 身份验证部分添加一些内容。
Facebook provides a Javascript SDKwhich you can use to get the access token on client-end directly, which is then passed to the server and used to further pull all the user information from Facebook. So you don't need any re-directs basically.
Facebook 提供了一个Javascript SDK,您可以使用它直接在客户端获取访问令牌,然后将其传递给服务器并用于进一步从 Facebook 中提取所有用户信息。所以你基本上不需要任何重定向。
Moreover, you can use the same API end-point for mobile applications as well. Just use the Android / iOS SDK for Facebook, obtain the Facebook access_token on the client end and pass it to the server.
此外,您还可以为移动应用程序使用相同的 API 端点。只需使用Android / iOS SDK for Facebook,在客户端获取Facebook access_token并传递给服务器即可。
Regarding the stateless natureas explained, when get_access_token is used to generate a token and passed to the client, this token is also stored on the server. So it's as good as a session token and I believe this makes it stateful ?
关于无状态性质,当使用 get_access_token 生成令牌并传递给客户端时,该令牌也存储在服务器上。所以它和会话令牌一样好,我相信这使它有状态?
Just my 2 cents..
只有我的 2 美分..
回答by myusuf
Here is an awesome article I found that can help you authenticate with:
这是我发现的一篇很棒的文章,可以帮助您进行身份验证:
- Local Auth
- 推特
- 谷歌
- 本地认证

