使用Passport和OAuth2 +社交networking的NodeJS REST身份validation

我正在使用NodeJS处理REST API。 为了validation,我决定使用Passport 。 我想要真正的RESTful API。 所以这意味着我必须使用令牌而不是会话。

我想让用户使用用户名和密码login,或使用Facebook,Google和Twitter等社交networking。

我使用我自己的OAuth2.0服务器来使用oauth2orize模块发出AccessRefresh tokens 。 所以,现在我可以注册新用户,然后颁发他们的令牌。 我遵循这个教程:

http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/

validation路由的用户:

 // api ------------------------------------------------------------------------------------ app.get('/api/userInfo', passport.authenticate('bearer', { session: false }), function(req, res) { // req.authInfo is set using the `info` argument supplied by // `BearerStrategy`. It is typically used to indicate scope of the token, // and used in access control checks. For illustrative purposes, this // example simply returns the scope in the response. res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope }) } ); 

所有这些工作都很好。 不幸的是我不知道如何实现社交authentication。

我正在阅读本教程:

 http://scotch.io/tutorials/javascript/easy-node-authentication-facebook 

但是在本教程中,他们并没有制定一个真正的RESTful API。 我已经根据本教程实施了用户架构,其中本地用户的令牌存储在分离的模型中。

 // define the schema for our user model var userSchema = mongoose.Schema({ local: { username: { type: String, unique: true, required: true }, hashedPassword: { type: String, required: true }, created: { type: Date, default: Date.now } }, facebook: { id: String, token: String, email: String, name: String }, twitter: { id: String, token: String, displayName: String, username: String }, google: { id: String, token: String, email: String, name: String } }); 

但现在,我该如何validation用户?

 passport.authenticate('bearer', { session: false }), 

这是validation只有持票人令牌反对我的数据库,但我怎么能validation社交令牌? 我错过了什么吗?

我使用Facebooklogin为我自己的RESTful API为我的记事本应用程序在这里 。 我开始将这个应用程序作为一个网页使用,但是login后的通信仍然是通过API进行的。

然后,我决定创build一个将使用API 的相同应用程序的移动版本 。 我决定这样做:移动应用程序通过Facebooklogin并发送Facebook用户ID和FB访问令牌给API,API调用Facebook的API来validation这些参数,如果成功注册一个新用户(或login现有的)在我的应用程序的数据库中,为此用户创build一个自定义标记并将其返回给移动应用程序。 从这里开始,移动应用程序发送这个自定义令牌来通过API来validation应用程序。

这是一些代码:

API中的auth(使用fbgraph npm模块):

 var graph = require('fbgraph'), Promise = require('bluebird') ... Promise.promisify(graph.get); ... var postAuthHandler = function (req, res) { var fbUserId = req.body.fbId, fbAccessToken = req.body.fbAccessToken, accessToken = req.body.accessToken; ... graph.setAppSecret(config.facebook.app.secret); graph.setAccessToken(fbAccessToken); var graphUser; var p = graph.getAsync('me?fields=id,name,picture') .then(function (fbGraphUser) { //when the given fb id and token mismatch: if (!fbGraphUser || fbGraphUser.id !== fbUserId) { console.error("Invalid user from fbAccessToken!"); res.status(HttpStatus.FORBIDDEN).json({}); return p.cancel(); } graphUser = fbGraphUser; return User.fb(fbUserId); }) .then(function (user) { if (user) { //user found by his FB access token res.status(HttpStatus.OK).json({accessToken: user.accessToken}); //stop the promises chain here return p.cancel(); } ...create the user, generate a custom token and return it as above... 

https://github.com/iliyan-trifonov/notepads-nodejs-angularjs-mongodb-bootstrap/blob/6617a5cb418ba8acd6351ef9a9f69228f1047154/src/routes/users.js#L46

用户模型:

 var userSchema = new mongoose.Schema({ facebookId: { type: String, required: true, unique: true }, accessToken: { type: String, required: true, unique: true }, name: { type: String, required: true }, photo: { type: String, required: true }, categories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Category' }], notepads: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Notepad' }] }); 

https://github.com/iliyan-trifonov/notepads-nodejs-angularjs-mongodb-bootstrap/blob/master/src/models/user.js#L9

移动应用程序中的Facebook身份validation:

  auth: function(fbId, fbAccessToken) { return $http({ url: apiBase + '/users/auth', data: { fbId: fbId, fbAccessToken: fbAccessToken }, method: 'POST', cache: false }); }, ... 

https://github.com/iliyan-trifonov/notepads-ionic/blob/master/www/js/services.js#L33

移动应用程序发送带有请求的令牌:

  notepads: { list: function() { return $http({ url: apiBase + '/notepads?insidecats=1' + '&token=' + User.get().accessToken/*gets the token from the local storage*/, method: 'GET', cache: false }); }, 

这是一个离子/angular/ Cordova应用程序。 来自移动应用程序的Facebooklogin启动安装在您的手机上的Facebook应用程序或打开popup窗口login到Facebook。 然后callback将Facebook用户的ID和访问令牌返回到我的移动应用程序。

fbgraph npm模块: https : //github.com/criso/fbgraph

我们创build了以下模式:

 var userSchema = mongoose.Schema({ local: { username: String, password: String }, facebook: { id: String, token: String, email: String, name: String }, google: { id: String, token: String, email: String, name: String }, token: { type: Schema.Types.ObjectId, ref: 'Token', default: null } }); var tokenSchema = mongoose.Schema({ value: String, user: { type: Schema.Types.ObjectId, ref: 'User' }, expireAt: { type: Date, expires: 60, default: Date.now } }); 

当login到我的networking应用程序时,我使用了像Facebook /谷歌PassportJS社会插件。 例:

 //auth.js(router) router.get('/facebook', passport.authenticate('facebook', {scope: ['email']})); router.get('/facebook/callback', passport.authenticate('facebook', { successRedirect: '/profile', failureRedirect: '/' })); 

现在,当我想浏览我的Web应用程序时,我使用通过Facebook插件提供给我的会话身份validation。 当用户想要请求API令牌时,他们需要login,这样我可以将该令牌与用户关联起来。

所以现在用户有一个与他们关联的令牌,他们可以使用我的API的令牌authentication。

我的API路由不关心或看看会话,他们关心的只是令牌。 通过创build一个快速路由器,并使用护照的承载策略作为所有路由到我的API的中间件来完成这个工作。

 //api.js (router) //router middleware to use TOKEN authentication on every API request router.use(passport.authenticate('bearer', { session: false })); router.get('/testAPI', function(req, res){ res.json({ SecretData: 'abc123' }); }); 

所以现在我只对我的API使用令牌authentication(不会查看会话数据)和会话authentication来轻松地导航我的webapp。 下面显示了我使用会话导航我的应用程序的一个示例:

 //secure.js(router) - Access private but NON-API routes. //router middleware, uses session authentication for every request router.use(function(req, res, next){ if(req.isAuthenticated()){ return next(); } res.redirect('/auth'); }); //example router router.get('/profile', function(req, res){ res.send("Private Profile data"); }); 

希望这会帮助你!

社交媒体的护照战略取决于会议。 他们不会没有一个function。

我正在运行int相同的问题,我希望我的REST服务器是无状态的。

在我看来,有2个选项。

  1. 将会话添加到您的其余服务器
  2. 添加一个新的auth服务器,负责创build和分发令牌。

PS:你应该把这个标记为护照,以便通行端口的开发者可以看到它。

如果您使用不记名令牌,则只需将唯一标识符传递给API的用户。 这很可能是以login的forms在一个单独的电话中完成的。 然后,每次调用API都要求在数据库中存在validation令牌的令牌,并更新其“生存时间”值。 在您的模式中,您不需要每个login名中的单个令牌,都需要单个具有生存时间值(TTL)的令牌模式,