Аутентификация NodeJS REST с использованием социальной сети Passport и 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 }),
это проверяет только токен-носитель против моего db, но как я могу проверить социальные токены? Я что-то упускаю?
Ответы
Ответ 1
Я использую Facebook для моего собственного RESTful API для моего приложения "Блокноты" здесь. Я запустил приложение как приложение, которое будет использоваться в качестве веб-страницы, но все же связь после входа будет через API.
Затем я решил создать мобильную версию того же приложения, которая будет использовать API. Я решил сделать так: мобильное приложение регистрируется через Facebook и отправляет идентификатор пользователя facebook и токен доступа FB к API, API вызывает API Facebooks для проверки этих параметров и, если он успешно регистрирует нового пользователя (или регистрирует существующий) в моей БД приложения, создает пользовательский токен для этого пользователя и возвращает его в мобильное приложение. Отсюда мобильное приложение отправляет этот пользовательский токен для аутентификации приложения с помощью 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: 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
});
},
Это приложение Ionic/ Angular/Cordova. Вход в Facebook из мобильного приложения запускает приложение Facebook, установленное на вашем телефоне, или открывает всплывающее окно для входа в Facebook. Затем обратный вызов возвращает идентификатор пользователя Facebook и токен доступа к моему мобильному приложению.
Модуль fpm gpm: https://github.com/criso/fbgraph
Ответ 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 Social, например 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 (никогда не смотрю на данные сеанса) и аутентификацию сеанса, чтобы легко перемещаться по моему 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");
});
Надеюсь, это поможет вам!
Ответ 3
Стратегии социальных медиа для паспорта зависят от сессии. Они не будут работать без одного.
Я запускаю одну и ту же проблему, я хочу, чтобы мой REST-сервер был неактивным.
По-моему, есть 2 варианта.
- Добавить сеансы на сервер отдыха
- Добавить новый сервер auth, который отвечает за создание и передачу токена.
PS: Вы должны пометить его как паспорт, чтобы его могли видеть разработчики порта.
Ответ 4
если вы используете токен-носитель, все, что вам нужно сделать, это передать уникальный идентификатор пользователю API. Это, скорее всего, будет сделано в одном единственном звонке, возможно, в форме входа. Тогда для каждого вызова API требуется, чтобы токен, который проверяет токен, существует в базе данных и обновляет значение Time To Live. Вам не нужны индивидуальные токены в вашей схеме для каждого входа, для чего вам нужна отдельная схема для токенов, у которой есть значение времени для жизни (TTL)