Аутентификация NodeJS REST с использованием паспорта и социальной сети OAuth2 +

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

Я хочу, чтобы пользователи войти, используя имя пользователя и пароль, или с помощью социальных сетей, таких как 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 }),

это проверка только токена носителя против моей БД, но как я могу проверить социальные токены? Я что-то упускаю?

4 ответов


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

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

вот код:

аутентификация в API (использует модуль 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 .

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

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

это ионный / угловой / Cordova приложение. Логин Facebook от мобильного приложения запускает приложение Facebook, установленное на вашем телефоне или открывает всплывающее окно для входа в Facebook. Затем обратный вызов возвращает идентификатор пользователя Facebook и маркер доступа к моему мобильному приложению.

в fbgraph модуль npm:https://github.com/criso/fbgraph


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, он должен войти в систему, чтобы я мог связать этот маркер с пользователем.

Итак, теперь у пользователя есть маркер связанные с ними, которые они могут использовать для аутентификации токенов для моего 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(никогда не смотрит на данные сеанса) и аутентификацию сеанса, чтобы легко перемещаться по моему 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");
    });

надеюсь, это поможет вам!


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

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

на мой взгляд есть 2 варианта.

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

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


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