Аутентификация через социальные сети для приложений Node.js при помощи Passport
Russian (Pусский) translation by AlexBioJS (you can also view the original English article)
То, что пароли в своей основе ненадежны по природе является уже давно установленным фактом. Поэтому просьба конечных пользователей о создании надежных паролей для каждого приложения, которое они используют, просто ухудшает ситуацию.
Простым выходом из ситуации является предоставление пользователям возможности пройти аутентификацию при помощи имеющихся у них аккаунтов в социальных сетях, например: Facebook, Twitter, Google и т.д. В этой статье мы собираемся осуществить как раз это и добавить возможность входа через социальные сети в пример приложения Node.js, созданного в первой части этой серии, посвященной аутентификации. В результате у нас будет возможность пройти аутентификацию за счет наших аккаунтов Facebook и Twitter при помощи промежуточного ПО (* программное обеспечение. Здесь и далее примеч. пер.) Passport.
Если вы не ознакомились с предыдущей статьей, то я рекомендовал бы вам тщательно изучить ее, поскольку мы будем работать в этой статье на основе базы, заложенной в предыдущей статье, и расширять ее функциональные возможности за счет добавления новых стратегий, маршрутов и представлений.
Вход через социальные сети
Для тех, кто не в курсе, возможность входа через социальные сети является типом Single Sign-on (* технологии единого входа) за счет использования имеющейся информации со сайтов социальных сетей, например: Facebook, Twitter и т.д., что предполагает наличие у пользователей уже созданных аккаунтов.
Возможность входа через социальные сети реализуется главным образом за счет какой-либо схемы аутентификации, например OAuth 2.0. Для подробного ознакомления с разными потоками (* метод получения токена (уникального идентификатора) для авторизации) входа, которые поддерживает OAuth, прочитайте эту статью. Мы отдали предпочтение Passport для реализации возможности входа через социальные сети для нас, поскольку он предоставляет разнообразные модули для множества провайдеров OAuth, будь то Facebook, Twitter, Google, GitHub и т.д. В данной статье мы будем использовать модули passport-facebook и passport-twitter для предоставления возможности входа при помощи имеющихся у пользователей аккаунтов в Facebook или Twitter.
Аутентификация через Facebook
Для того, чтобы сделать возможной аутентификацию через Facebook, нам для начала необходимо создать приложение Facebook при помощи Портала Facebook для разработчиков. Запишите App ID и App Secret и укажите URL-адрес обратного вызова, перейдя в Settings и указав значение в поле Site URL раздела Website для приложения. Также убедитесь, что ввели корректный адрес электронной почты в поле Contact Email. Он необходим для того, чтобы сделать приложение общедоступным.
Далее перейдите в раздел Status & Review (* App Review) и переключите ползунок в положение Yes, чтобы сделать приложение доступным для пользователей. Мы создаем конфигурационный файл (* файл, с данными о предыдущей, текущей или будущей конфигурации системы или приложения), fb.js, для размещения информации о конфигурации приложения, которая будет необходима для подключения к Facebook.
1 |
// facebook app settings - fb.js |
2 |
module.exports = { |
3 |
'appID' : '<your_app_identifier>', |
4 |
'appSecret' : '<your_app_secret>', |
5 |
'callbackUrl' : 'http://localhost:3000/login/facebook/callback' |
6 |
}
|
Стратегия входа через Facebook
Теперь переходим обратно к нашему приложению и определяем стратегию Passport для аутентификации через Facebook при помощи модуля FacebookStrategy, используя вышеуказанные настройки для получения профиля пользователя Facebook и показа его данных в представлении.
1 |
passport.use('facebook', new FacebookStrategy({ |
2 |
clientID : fbConfig.appID, |
3 |
clientSecret : fbConfig.appSecret, |
4 |
callbackURL : fbConfig.callbackUrl |
5 |
},
|
6 |
|
7 |
// facebook will send back the tokens and profile
|
8 |
function(access_token, refresh_token, profile, done) { |
9 |
// asynchronous
|
10 |
process.nextTick(function() { |
11 |
|
12 |
// find the user in the database based on their facebook id
|
13 |
User.findOne({ 'id' : profile.id }, function(err, user) { |
14 |
|
15 |
// if there is an error, stop everything and return that
|
16 |
// ie an error connecting to the database
|
17 |
if (err) |
18 |
return done(err); |
19 |
|
20 |
// if the user is found, then log them in
|
21 |
if (user) { |
22 |
return done(null, user); // user found, return that user |
23 |
} else { |
24 |
// if there is no user found with that facebook id, create them
|
25 |
var newUser = new User(); |
26 |
|
27 |
// set all of the facebook information in our user model
|
28 |
newUser.fb.id = profile.id; // set the users facebook id |
29 |
newUser.fb.access_token = access_token; // we will save the token that facebook provides to the user |
30 |
newUser.fb.firstName = profile.name.givenName; |
31 |
newUser.fb.lastName = profile.name.familyName; // look at the passport user profile to see how names are returned |
32 |
newUser.fb.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first |
33 |
|
34 |
// save our user to the database
|
35 |
newUser.save(function(err) { |
36 |
if (err) |
37 |
throw err; |
38 |
|
39 |
// if successful, return the new user
|
40 |
return done(null, newUser); |
41 |
});
|
42 |
}
|
43 |
});
|
44 |
});
|
45 |
}));
|
Настройка маршрутов
Теперь нам необходимо добавить определенные маршруты для реализации возможности входа при помощи Facebook и для обработки обратного вызова после авторизации пользователя в приложении для того, чтобы использовать его или ее данные аккаунта Facebook.
1 |
// route for facebook authentication and login
|
2 |
// different scopes while logging in
|
3 |
router.get('/login/facebook', |
4 |
passport.authenticate('facebook', { scope : 'email' } |
5 |
));
|
6 |
|
7 |
// handle the callback after facebook has authenticated the user
|
8 |
router.get('/login/facebook/callback', |
9 |
passport.authenticate('facebook', { |
10 |
successRedirect : '/home', |
11 |
failureRedirect : '/' |
12 |
})
|
13 |
);
|
Страница для входа нашей демоверсии приложения выглядит следующим образом:



При нажатии кнопки Login with Facebook будет осуществлена попытка аутентификации через Facebook. Если вы уже входили в Facebook, то вам будет выведен нижеследующий диалог, в котором запрашивается ваше разрешение на получение доступа к данным вашего профиля и адресу электронной почты, или же вас попросят войти в Facebook и затем будет показан этот диалог.



Если вы дали разрешение на получение доступа к данным вашего профиля и адресу электронной почты, то будет вызвана наша зарегистрированная функция обратного вызова с данными пользователя в качестве параметров. В зависимости от того, что вы хотите делать с информацией, вы можете их сохранить, чтобы использовать позже или показать, или же просто проигнорировать. Если хотите, то можете заранее забежать вперед и ознакомиться с полным кодом в этом репозитории git.
Уместно заметить, что помимо основной информации, которую предоставляет эта демоверсия приложения, вы могли бы использовать тот же самый механизм аутентификации для извлечения более полезной информации о пользователе, например, его список друзей, за счет указания соответствующей области действия и использования Facebook API с токеном доступа, полученным вместе с данными профиля пользователя.
Аутентификация через Twitter
Для осуществления аутентификации через Twitter необходимо подключить подобный модуль для реализации аутентификации, и Passport помогает в этом за счет своего модуля passport-twitter.
Для начала вам необходимо создать новое приложение Twitter при помощи интерфейса Управления приложением. Один момент, на который стоит здесь обратить внимание, - это то, что при указании URL обратного вызова Twitter не работает гладко с ним, если в поле для ввода URL обратного вызова указано "localhost". Для преодоления этой трудности при разработке вы могли бы использовать специальный адрес обратной связи, или "127.0.0.1" вместо "localhost". После создания приложения запишите следующие ключ API и секретную информацию в конфигурационный файл таким образом:
1 |
// twitter app settings - twitter.js
|
2 |
module.exports = { |
3 |
'apikey' : '<your_app_key>', |
4 |
'apisecret' : '<you_app_secret>', |
5 |
'callbackUrl' : 'http://127.0.0.1:3000/login/twitter/callback' |
6 |
}
|
Стратегия входа через Twitter
Стратегия входа через Twitter является экземпляром TwitterStrategy и имеет следующий вид:
1 |
passport.use('twitter', new TwitterStrategy({ |
2 |
consumerKey : twitterConfig.apikey, |
3 |
consumerSecret : twitterConfig.apisecret, |
4 |
callbackURL : twitterConfig.callbackURL |
5 |
},
|
6 |
function(token, tokenSecret, profile, done) { |
7 |
// make the code asynchronous
|
8 |
// User.findOne won't fire until we have all our data back from Twitter
|
9 |
process.nextTick(function() { |
10 |
|
11 |
User.findOne({ 'twitter.id' : profile.id }, |
12 |
function(err, user) { |
13 |
// if there is an error, stop everything and return that
|
14 |
// ie an error connecting to the database
|
15 |
if (err) |
16 |
return done(err); |
17 |
|
18 |
// if the user is found then log them in
|
19 |
if (user) { |
20 |
return done(null, user); // user found, return that user |
21 |
} else { |
22 |
// if there is no user, create them
|
23 |
var newUser = new User(); |
24 |
|
25 |
// set all of the user data that we need
|
26 |
newUser.twitter.id = profile.id; |
27 |
newUser.twitter.token = token; |
28 |
newUser.twitter.username = profile.username; |
29 |
newUser.twitter.displayName = profile.displayName; |
30 |
newUser.twitter.lastStatus = profile._json.status.text; |
31 |
|
32 |
// save our user into the database
|
33 |
newUser.save(function(err) { |
34 |
if (err) |
35 |
throw err; |
36 |
return done(null, newUser); |
37 |
});
|
38 |
}
|
39 |
});
|
40 |
});
|
41 |
})
|
42 |
);
|
Настройка маршрутов
1 |
// route for twitter authentication and login
|
2 |
// different scopes while logging in
|
3 |
router.get('/login/twitter', |
4 |
passport.authenticate('twitter') |
5 |
);
|
6 |
|
7 |
// handle the callback after facebook has authenticated the user
|
8 |
router.get('/login/twitter/callback', |
9 |
passport.authenticate('twitter', { |
10 |
successRedirect : '/twitter', |
11 |
failureRedirect : '/' |
12 |
})
|
13 |
);
|
14 |
|
15 |
/* GET Twitter View Page */
|
16 |
router.get('/twitter', isAuthenticated, function(req, res){ |
17 |
res.render('twitter', { user: req.user }); |
18 |
});
|
Теперь для того чтобы протестировать приложение, убедитесь, что вы используете http://127.0.0.1: <port> , а не http://localhost: <port>. Как мы упоминали выше, похоже, что возникает проблема при обмене токенами с Twitter, если в качестве имени хоста указано "localhost". При нажатии кнопки Login with Twitter, как и ожидалось, происходит запрос у пользователя разрешения на использование данных аккаунта пользователя Twitter.



Если вы позволяете приложению использовать данные своего аккаунта Twitter и другой информации с ограниченным доступом, происходит вызов функции обратного вызова, зарегистрированной в стратегии входа, которая затем используется для сохранения этих данных в базе на стороне сервера.
Заключение
Вот и все!! Мы успешно добавили возможности входа через Facebook и Twitter в наш пример приложения без написания большого количества кода и не связываясь со сложностями, связанными с механизмом аутентификации, передав тяжелую работу Passport. Подобные стратегии входа могут быть написаны для множества провайдеров, поддерживаемых Passport. С кодом для всего приложения можно ознакомиться в этом репозитории git. Если хотите, то можете расширить его возможности и использовать его в ваших собственных проектах.



