19

我正在使用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

回答

6

我正在使用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

0

護照的社交媒體策略取決於會話。沒有一個,它們就不會運作。

我正在運行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

2

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

0

如果您使用持票人令牌,您只需將唯一標識符傳遞給API的用戶。這很可能是以登錄的形式在一個單獨的電話中完成的。然後,每次調用API都需要驗證令牌的令牌存在於數據庫中,並更新其「生存時間」值。您不需要在架構中爲您的每個登錄需要單獨的具有生存時間值(TTL)的令牌的架構

相關問題