19

Я работаю над REST api, используя NodeJS. Для аутентификации я решил использовать Passport. Я хочу по-настоящему RESTful api. Поэтому это означает, что я должен использовать токены вместо сеансов.Аутентификация NodeJS REST с использованием паспорта и социальной сети OAuth2 +

Я хочу, чтобы пользователи входили в систему, используя имя пользователя и пароль, или используя социальные сети, такие как Facebook, Google и Twitter.

Я делаю свой собственный OAuth2.0 сервер для выдачи Access и Refresh 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

Вы можете уточнить, как вы в конечном итоге решить это? Можете ли вы оставить свой твиттер за дальнейшей перепиской. Спасибо :) –

+0

, а вместо учебника александров, почему нельзя использовать паспорт? http://passportjs.org/guide/oauth/ –

+0

Также зачем реализовать oAuth для своего собственного сервера? почему бы не использовать паспорт-локальный, а затем выдавать токены по стратегии-носителям? –

ответ

6

Я пользуюсь Facebook для моего собственного API RESTful для my Notepads app here. Я запустил приложение как приложение, которое будет использоваться в качестве веб-страницы, но все же связь после входа будет через API.

Тогда я решил создать mobile version of the same app, который будет использовать API. Я решил сделать это так: мобильное приложение регистрируется через Facebook и отправляет идентификатор пользователя facebook и токен доступа FB к API, API вызывает API Facebooks для проверки этих параметров и, если успешно регистрирует нового пользователя (или регистрирует существующий) в БД моего приложения, создает пользовательский токен для этого пользователя и возвращает его в мобильное приложение. Отсюда мобильное приложение отправляет этот пользовательский токен для аутентификации приложения с помощью API.

Вот код:

Срок авторизации в API (использует модуль НПМ 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.

Модель пользователя:

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.

The Facebook аутентификации в мобильном приложении:

   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 и токен доступа к моему мобильному приложению.

fbgraph НПМ модуль: https://github.com/criso/fbgraph

+0

Просто отформатируйте URL-адреса, и они будут активны. Для этого не требуется никаких разрешений. –

+0

Спасибо, @NickVolynkin! Вчера я смог сделать только первые 2 URL-адреса активными и получил предупреждение, что мне нужно как минимум 10 человек. для остальных. Но сегодня у меня это есть и отформатирован URL. –

0

Стратегии социальных медиа для паспорта зависят от сеанса. Они не будут работать без одного.

Я использую ту же проблему, что я хотел, чтобы мой сервер REST был без гражданства.

По-моему, есть 2 варианта.

  1. Добавить сеансы на свой сервер отдыха
  2. Добавить новый сервер аутентификации, который отвечает за создание и раздавая маркера.

PS: Вы должны пометить его как паспорт, чтобы его могли видеть передатчики портов.

+0

Вместо учебника по александрову (http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/) почему вы не можете использовать паспорт? http://passportjs.org/guide/oauth/ –

+0

Это модуль, который использует учебник под капотом. Это тот, который использует сеанс. Токены хранятся в сеансе, поэтому при переадресации браузера он может связывать возвращаемый пользователь/токен. По этой причине моя служба аутентификации является государственной, и мой api не имеет гражданства. –

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

При входе в моем веб-приложение, я использую PassportJS Социальный плагин, как facebook/Google. Пример:

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

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

Теперь, когда я хочу, чтобы провести мой веб-приложение, я использую проверки подлинности сеанса, который был сделан доступным для меня через плагин Facebook. Когда пользователь хочет запросить маркер API, он должен быть зарегистрирован, поэтому я могу связать этот токен с пользователем.

Итак, теперь у пользователя есть связанный с ними токен, который они могут использовать для аутентификации Token для моего API.

Мои маршруты API не заботятся или не смотрят на сеансы, все, о чем они заботятся, это токен. Iv'e сделал это, создав экспресс-маршрутизатор и используя стратегию переноса паспорта как промежуточное ПО для всех маршрутов, направляющихся к моему 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, (ли когда-либо не смотреть на данные сессии) легко перемещаться мой веб-приложение. Пример использования сеансов для навигации по моему приложению приведен ниже:

//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, например, от устройства Android. Для этого должен существовать механизм RESTful. После этого ваше решение идеально подходит, потому что маркеры-носители берут на себя, чтобы генерировать специальные токены приложений. поэтому я оставил эту мысль в своем видео https://www.youtube.com/watch?v=VVFedQZGUgw –

0

Если вы используете токен-носитель, все, что вам нужно сделать, это передать уникальный идентификатор пользователю API. Это, скорее всего, будет сделано в одном единственном звонке, возможно, в форме входа. Тогда для каждого вызова API требуется, чтобы токен, который проверяет токен, существует в базе данных и обновляет значение Time To Live. Вам не нужны отдельные токены в вашей схеме для каждого входа, для чего вам нужна отдельная схема для токенов, у которых есть значение времени жизни (TTL)