19

Tôi đang làm việc trên REST api sử dụng NodeJS. Để xác thực, tôi quyết định sử dụng Passport. Tôi muốn api thực sự yên tĩnh. Vì vậy, nó có nghĩa là tôi phải sử dụng thẻ thay vì phiên.Xác thực NodeJS REST sử dụng mạng xã hội Hộ chiếu và OAuth2 +

Tôi muốn cho phép người dùng đăng nhập bằng tên người dùng và mật khẩu hoặc sử dụng các mạng xã hội như Facebook, Google và Twitter.

Tôi tạo máy chủ OAuth2.0 của riêng mình để phát hành AccessRefresh tokens sử dụng mô-đun oauth2orize. Vì vậy, bây giờ tôi có thể đăng ký người dùng mới và sau đó phát hành chúng. Tôi đi theo hướng dẫn này:

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

Thẩm định sử dụng cho tuyến đường:

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

làm việc Tất cả điều này khá tốt. Rất tiếc, tôi không biết cách triển khai xác thực xã hội.

Tôi đã đọc hướng dẫn này:

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

Nhưng trong hướng dẫn này chúng không được thực hiện một api thực sự yên tĩnh. Tôi đã triển khai lược đồ người dùng theo hướng dẫn này, nơi các mã thông báo cho người dùng cục bộ được lưu trữ trong các mô hình riêng biệt.

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

Nhưng bây giờ, làm cách nào để xác minh người dùng?

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

điều này chỉ xác minh mã xác nhận đối với db của tôi, nhưng làm cách nào để xác minh mã thông báo xã hội? Tui bỏ lỡ điều gì vậy?

+0

bạn có thể giải thích cách cuối cùng bạn giải quyết vấn đề này không? Bạn cũng có thể để lại quyền xử lý Twitter của bạn ở phía sau để trao đổi thêm. Cảm ơn :) –

+0

và thay vì hướng dẫn aleksandrov, tại sao không thể sử dụng hộ chiếu-oauth? http://passportjs.org/guide/oauth/ –

+0

Ngoài ra tại sao triển khai oAuth cho máy chủ của riêng bạn? tại sao không sử dụng hộ chiếu địa phương và sau đó phát hành thẻ bằng chiến lược mang? –

Trả lời

6

Tôi đang sử dụng đăng nhập Facebook cho API RESTful của riêng mình cho my Notepads app here. Tôi bắt đầu ứng dụng như một ứng dụng sẽ được sử dụng như một trang web nhưng vẫn là thông tin liên lạc sau khi đăng nhập sẽ được thông qua API.

Sau đó, tôi quyết định tạo một mobile version of the same app sẽ sử dụng API. Tôi quyết định làm theo cách đó: Ứng dụng di động đăng nhập thông qua Facebook và gửi ID người dùng facebook và mã thông báo truy cập FB tới API, API gọi API của Facebook để xác minh các tham số này và nếu đăng ký thành công người dùng mới (hoặc đăng nhập vào hiện có) trong DB của ứng dụng của tôi, tạo mã thông báo tùy chỉnh cho người dùng này và trả lại cho ứng dụng dành cho thiết bị di động. Từ đây, ứng dụng di động gửi mã thông báo tùy chỉnh này để xác thực ứng dụng bằng API.

Dưới đây là một số mã:

Các auth trong API (sử dụng các mô-đun NPM fbgraph):

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.

Mô hình tài:

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.

Các Facebook auth trong ứng dụng di động:

   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.

Ứng dụng di động gửi các mã thông báo với các yêu cầu:

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

Đây là một Ionic/góc/Cordova ứng dụng. Đăng nhập Facebook từ ứng dụng di động sẽ khởi động ứng dụng Facebook được cài đặt trên điện thoại của bạn hoặc mở một cửa sổ bật lên để đăng nhập vào Facebook. Sau đó, một cuộc gọi lại trả về id người dùng và mã thông báo truy cập của Facebook cho ứng dụng di động của tôi.

Mô đun fbgraph npm: https://github.com/criso/fbgraph

+0

Chỉ cần định dạng URL và chúng sẽ hoạt động. Không có quyền cần thiết cho việc này. –

+0

Cảm ơn, @NickVolynkin! Hôm qua tôi chỉ có thể làm cho 2 url đầu tiên hoạt động và nhận được cảnh báo rằng tôi cần ít nhất 10 đại diện. cho phần còn lại. Nhưng hôm nay tôi có nó và định dạng các url thành công. –

0

Chiến lược truyền thông xã hội cho hộ chiếu tùy thuộc vào phiên. Chúng sẽ không hoạt động nếu không có.

Tôi đang chạy int o cùng một vấn đề tôi muốn máy chủ REST của mình không có trạng thái.

Theo ý kiến ​​của tôi, có 2 tùy chọn.

  1. Thêm phiên đến máy chủ còn lại của bạn
  2. Thêm một máy chủ auth mới có trách nhiệm cho việc tạo ra và phân phát thẻ.

PS: Bạn nên gắn thẻ thẻ này làm hộ chiếu để chuyển qua nhà phát triển cổng có thể nhìn thấy nó.

+0

Thay vì hướng dẫn aleksandrov (http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/) tại sao không thể sử dụng hộ chiếu-oauth? http://passportjs.org/guide/oauth/ –

+0

Đó là mô-đun hướng dẫn sử dụng dưới mui xe. Đây là phiên sử dụng phiên. Mã thông báo được lưu trữ trong phiên để khi trình duyệt chuyển hướng, nó có thể liên kết người dùng/mã thông báo được trả lại. Dịch vụ auth của tôi là nhà nước vì lý do này và api của tôi là vô quốc tịch. –

2

Iv'e tạo Schema của sau:

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

Khi đăng nhập vào ứng dụng web của tôi, tôi sử dụng một plugin PassportJS xã hội như facebook/google. Ví dụ:

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

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

Bây giờ khi tôi muốn điều hướng ứng dụng web của mình, tôi sử dụng xác thực phiên đã có sẵn cho tôi qua plugin Facebook. Khi người dùng muốn yêu cầu mã thông báo API, họ cần phải đăng nhập để tôi có thể liên kết mã thông báo đó với người dùng.

Vì vậy, bây giờ người dùng có mã thông báo được liên kết với họ mà họ có thể sử dụng để xác thực Mã thông báo cho API của tôi.

Tuyến đường API của tôi không quan tâm hoặc xem phiên, tất cả những gì họ quan tâm là mã thông báo. Iv'e đã ​​làm điều này bằng cách tạo một bộ định tuyến tốc hành và sử dụng chiến lược mang hộ chiếu làm phần mềm trung gian cho tất cả các tuyến đường hướng đến API của tôi.

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

Vì vậy, bây giờ tôi chỉ sử dụng xác thực mã thông báo cho API của tôi (Không bao giờ xem dữ liệu phiên) và Xác thực phiên để dễ dàng điều hướng ứng dụng web của tôi. Ví dụ về tôi sử dụng các phiên để điều hướng ứng dụng của tôi được hiển thị bên dưới:

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

Hy vọng điều này sẽ giúp bạn!

+2

Cảm ơn bạn lần nữa! Vâng, tôi đã hiểu khái niệm của bạn. Tuy nhiên điều này không làm cho backend tinh khiết RESTful - không có điểm vào cho auth facebook từ nói một thiết bị Android. Nên có một cơ chế RESTful để làm điều này. Sau này giải pháp của bạn là hoàn hảo, bởi vì mã thông báo mang về để tạo ra các thẻ cụ thể của ứng dụng. do đó tôi đã để ý nghĩ này trong video của bạn https://www.youtube.com/watch?v=VVFedQZgUgw –

0

nếu bạn sử dụng mã thông báo người mang, tất cả những gì bạn phải làm là chuyển Mã định danh duy nhất cho người dùng API. Điều này rất có thể sẽ được thực hiện trong một cuộc gọi số ít có thể dưới dạng đăng nhập. Sau đó, mọi cuộc gọi tới API yêu cầu mã thông báo đó xác minh mã thông báo tồn tại trong cơ sở dữ liệu và cập nhật giá trị Thời gian sống của nó. Bạn không cần mã thông báo cá nhân trong lược đồ của mình cho từng thông tin đăng nhập, bạn cần có một lược đồ riêng cho các mã thông báo có Giá trị thời gian thực (TTL)

Các vấn đề liên quan