使用Passport和OAuth2 +社交网络的NodeJS REST身份验证

问题描述:

我正在使用NodeJSREST API。为了验证,我决定使用Passport。我想要真正的RESTful api。所以这意味着我必须使用令牌而不是会话。使用Passport和OAuth2 +社交网络的NodeJS REST身份验证

我想让用户使用用户名和密码登录,或使用Facebook,Google和Twitter等社交网络。

我制作我自己的OAuth2.0服务器用于发行AccessRefresh tokens使用oauth2orize模块。所以现在我可以注册新用户,然后发布它们的令牌。 我跟着这个教程:

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

验证用户的路线:

// 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 }) 
     } 
    ); 

这一切都工作得很好。不幸的是我不知道如何实现社交认证。

我阅读本教程:

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 
    } 
}); 

但现在,我该如何验证用户?

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

这是验证只有持票人令牌反对我的数据库,但我如何验证社交令牌?我错过了什么吗?

+0

你能详细说明你最终如何解决这个问题吗?你还可以留下你的Twitter手柄进一步通信。谢谢:) – 2015-04-15 21:29:32

+0

而不是aleksandrov教程,为什么不能使用passport-oauth? http://passportjs.org/guide/oauth/ – 2015-04-16 00:49:59

+0

也为什么为你自己的服务器实现oAuth?为什么不使用护照本地,然后通过载体策略发布令牌? – 2015-04-16 12:16:31

我正在使用Facebook登录为我自己的RESTful API my Notepads app here。我开始将应用程序作为一个将用作网页的应用程序,但仍然是通过API登录后的通信。

然后我决定创建一个使用API​​的mobile version of the same app。我决定这样做:移动应用程序通过Facebook登录并将Facebook用户ID和FB访问令牌发送到API,API调用Facebook的API来验证这些参数,并且如果成功注册新用户(或登录现有的)在我的应用程序的数据库中,为此用户创建一个自定义标记并将其返回给移动应用程序。从这里开始,移动应用程序发送这个自定义标记以通过API对应用程序进行身份验证

下面是一些代码:

API中的身份验证(使用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的AUTH在移动应用中:

   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 
       }); 
      }, 

这是一种离子/角/科尔多瓦应用程序。来自移动应用程序的Facebook登录启动安装在手机上的Facebook应用程序,或打开弹出窗口登录Facebook。然后回调将Facebook用户的ID和访问令牌返回给我的移动应用程序。

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

+0

只需格式化网址,它们就会处于活动状态。没有权限需要这个。 – 2015-06-08 23:38:14

+0

谢谢@NickVolynkin!昨天我只能做出前两个活跃的网站,并且收到一个警报,说我至少需要10个代表。对于其余的。但是今天,我已经成功地将这些网址格式化了。 – 2015-06-09 11:30:32

护照的社交媒体策略取决于会话。没有一个,它们就不会运作。

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

在我看来有2个选项。

  1. 加入会话您的休息服务器
  2. 添加一个新的auth服务器负责创建和发放令牌。

PS:您应该将其标记为passport,以便pass port devs可以看到它。

+0

而不是aleksandrov教程(http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/)为什么不能使用passport-oauth? http://passportjs.org/guide/oauth/ – 2015-04-16 00:50:41

+0

这是本教程所使用的模块。它是使用会话的人。令牌存储在会话中,以便在浏览器重定向时它可以关联返回的用户/令牌。由于这个原因,我的身份验证服务是有状态的,我的API是无状态的。 – 2015-04-16 14:00:58

Iv'e创建以下架构的:

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 
    } 
}); 

当登录到我的web应用程序,我用一个PassportJS社交插件,如Facebook /谷歌。示例:

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

router.get('/facebook/callback', 
    passport.authenticate('facebook', { successRedirect: '/profile', 
             failureRedirect: '/' })); 

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

因此,现在用户有一个与它们关联的令牌,它们可以用于我的API的令牌认证。

我的API路由不关心或看看会话,他们关心的只是令牌。通过创建一个快速路由器,并使用护照承载策略作为所有路由的中间件,向我的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的(不永远看会话数据)和会话认证轻松浏览我的web应用程序。我使用会话浏览我的应用程序的一个示例如下所示:

//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"); 
    }); 

希望这会帮助你!

+2

再次感谢!是的,我了解你的概念。然而,这并没有使后端纯粹的RESTful - 没有从说安卓设备的Facebook身份验证的入口点。应该有一个RESTful机制来做到这一点。在此之后,您的解决方案是完美的,因为持票人代币接管生成特定于应用程序的令牌。因此我在视频中留下了这个想法https://www.youtube.com/watch?v=VVFedQZgUgw – 2015-04-16 02:43:36

如果您使用持票人令牌,您只需将唯一标识符传递给API的用户。这很可能是以登录的形式在一个单独的电话中完成的。然后,每次调用API都需要验证令牌的令牌存在于数据库中,并更新其“生存时间”值。您不需要在架构中为您的每个登录需要单独的具有生存时间值(TTL)的令牌的架构