Использование социального входа с паспортом и узлом

  1. Логин в фейсбук
  2. Твиттер Войти

Я уже писал в блоге про паспорт ( «Несколько советов по паспорту» ) а также пример социального входа в систему с паспортом ( «Новый POC - Daily Reddit» ), но я подумал, что было бы неплохо поделиться примером, сфокусированным только на использовании социального входа Паспорт , Как я обнаружил, когда я впервые работал с Passport, это действительно облегчает задачу, но иногда бывает невероятно сложно найти нужную часть документов, чтобы определить конкретные потребности в использовании.

Для меня самой большой вещью, которая помогла мне, было осознание того, что я не могу полагаться на документацию на главном сайте Passport. Чтобы было ясно, это не плохо. Это просто неполноценно. Переход в репозиторий для стратегий, которые я использовал, помог мне заставить мой код работать, и я настоятельно рекомендую разработчикам сделать то же самое. Если вы не знаете, когда вы нажимаете «Стратегии» на сайте Passport, каждая стратегия перечисляет ссылки на соответствующий репозиторий для получения дополнительной информации.

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

Хорошо, так с этим из пути, позвольте мне начать с четкого документирования того, что я строю здесь:

  • У меня есть приложение Node, где человек может войти через Facebook или Twitter.
  • Когда они входят в систему, я хочу получить их адрес электронной почты.
  • Я хочу сохранить пользовательскую запись локально, используя Mongo, которая соответствует этому адресу электронной почты.

Довольно просто, правда? Давайте начнем с рассмотрения базовой оболочки приложения без какой-либо поддержки входа в систему. Как я уже сказал, я использую Mongo для моей настойчивости, и я решил использовать Мангуста также.

var express = require ('express'); var exphbs = require ('express-handlebars'); var user = require ('./ models / user'); var credentials = require ('./ credentials.json'); var app = express (); app.use (требуется ( 'куки-анализатор') (credentials.cookieSecret)); app.use (require ('express-session') ({resave: false, saveUninitialized: false, secret: credentials.cookieSecret})); app.engine ('handlebars', exphbs ({defaultLayout: 'main'})); app.set ('view engine', 'handlebars'); var mongoose = require ('mongoose'); var opts = {server: {socketOptions: {keepAlive: 1}}}; switch (app.get ('env')) {case 'development': mongoose.connect (credentials.mongo.development.connectionString, opts); перерыв; case 'production': mongoose.connect (credentials.mongo.production.connectionString, opts); перерыв; по умолчанию: выбрасывать новую ошибку ('Неизвестная среда выполнения:', app.get ('env')); } app.set ('port', process.env.PORT || 3000); app.use (express.static (__dirname + '/ public')); function isLoggedIn (req, res, next) {req.loggedIn = !! req.user; следующий(); } app.get ('/', isLoggedIn, function (req, res) {res.render ('index', {title: 'Welcome to X', loggedIn: req.loggedIn});}); app.get ('/ login', isLoggedIn, function (req, res) {if (req.loggedIn) res.redirect ('/'); console.log (req.loggedIn); res.render ('login', {title: 'Логин / Регистрация'});}); // 500 обработчик ошибок (промежуточное программное обеспечение) app.use (function (err, req, res, next) {console.error (err.stack); res.status (500); res.render ('error');}) ; app.listen (app.get ('port'), function () {console.log ('Экспресс работает на http: // localhost:' + app.get ('port'));});

Здесь нет ничего интересного. Вы можете видеть, как я загружаю свои учетные данные и подключаюсь к Монго. Я также использую Handlebars для моего движка шаблонов. Я использую сессии, чтобы распознать, когда вы вошли в систему. Я построил две страницы - домашнюю страницу и страницу входа. Страница входа просто предоставляет ссылки, чтобы начать аутентификацию через Twitter или Facebook. (В реальном приложении я бы, наверное, просто включил это в заголовок или что-то подобное.)

Давайте начнем с добавления поддержки Facebook.

Логин в фейсбук

Прежде чем я сделаю какой-либо код, мне нужно создать приложение Facebook, связанное с моим проектом. Это даст мне необходимые учетные данные и позволит моему приложению аутентифицироваться с ними.

Предполагая, что у вас есть учетная запись Facebook, зайдите на https://developers.facebook.com/. В правом верхнем углу будет кнопка «Мои приложения» - нажмите ее, чтобы добавить новое приложение.

Вам будет предложено ввести имя и категорию. Если тестируете, введите все, что вы хотите. Если вы создаете что-то для производства, на самом деле введите то, что имеет смысл.

После ввода Captcha на следующей странице нажмите Add Product. Справа находится продукт, который вы хотите - Facebook Login.

Для платформы выберите WWW:

Затем вам будет предложено ввести информацию о вашем сайте. Вам еще не нужно иметь сайт в производстве. Вы можете абсолютно использовать localhost для своих значений. Для первого приглашения я использовал http: // localhost: 3000, как то, что использовало мое приложение Express. Я пропустил остальные панели и, когда закончил, нажал на новую ссылку «Настройки» в группе «Вход в Facebook» в меню слева.

На странице настроек есть одна важная настройка: «Действительные URI перенаправления OAuth». Вы должны сообщить Facebook, куда пользователь может быть перенаправлен обратно после авторизации. Опять же, вы можете использовать localhost для этого. Я использовал http: // localhost: 3000 / auth / facebook / callback. Зачем? Вот что я видел в паспортных примерах. Это произвольно. Просто помните, что вам нужно будет добавить производственный URL позже.

Убедитесь, что вы нажали Сохранить! Затем перейдите по главной ссылке «Настройки» в левой навигационной панели (вверху), и вы увидите поле «Идентификатор приложения» и «Секрет приложения». Скопируйте их локально. Для меня я использую файл JSON. Вот файл (в Twitter уже есть, просто проигнорируйте;)

{"cookieSecret": "dfkjdlsfjljklsdfj", "facebook": {"app_id": "theidofallids", "app_secret": "astringishere", "callback": "http: // localhost: 3000 / auth / facebook / callback"} , "twitter": {"consumer_key": "akeyishere", "consumer_secret": "mysecretisbetterthanyoursecret", "callback": "http: // localhost: 3000 / auth / twitter / callback"}, "mongo": {"development ": {" connectionString ":" mongodb: // localhost / foo "}," production ": {}}}

Уф. Итак, на данный момент вы сделали то, что требуется на стороне Facebook. Теперь вернемся к коду. Вам нужно установить Passport (npm install --save passport), а затем стратегию Facebook (npm install --save passport-facebook).

Хорошо - теперь давайте пройдемся по коду. Когда я пишу в блоге, я, к вашему сведению, поделюсь всем app.js, поэтому не беспокойтесь, если вы немного заблудились. Во-первых, требуется в пакетах:

var passport = require ('passport'), FacebookStrategy = require ('passport-facebook'). Стратегия;

Далее мы настраиваем стратегию Facebook. Это включает в себя несколько действительно важных моментов, поэтому обратите особое внимание:

passport.use (new FacebookStrategy ({clientID: credentials.facebook.app_id, clientSecret: credentials.facebook.app_secret, callbackURL: credentials.facebook.callback, profileFields: ['id', 'displayName', 'emails']}, функция (accessToken, refreshToken, profile, done) {console.log (profile); var me = новый пользователь ({email: profile.emails [0] .value, name: profile.displayName}); / * сохранить, если новый * / user.findOne ({email: me.email}, function (err, u) {if (! u) {me.save (function (err, me) {if (err) return done done (err); done (null, me);});} else {console.log (u); done (null, u);}});}));

От верхней:

Во-первых, я предоставляю свои различные полномочия.

Далее я передаю необязательный (и не очень хорошо документированный) параметр, называемый profileFields. По умолчанию вы не получаете большую часть профиля при входе в систему. Я считаю, что id и displayName являются значениями по умолчанию, но мне определенно нужно было добавлять электронные письма. Помните, мой план - использовать электронную почту в качестве основного ключа для моих пользователей.

Объект профиля предоставлено паспортом и пытается объединить профили от различных поставщиков в один набор значений. Я беру значения из этого профиля, чтобы создать новый пользовательский объект. Я упоминал ранее, что использовал Mongoose для работы с Mongo, и он позволяет вам создавать объекты модели, чтобы вам было проще использовать CRUD для данных Mongo. Если вам интересно, вот эта модель:

var mongoose = require ('mongoose'); var userSchema = mongoose.Schema ({id: строка, электронная почта: строка, имя: строка}); var User = mongoose.model ('Пользователь', userSchema); module.exports = Пользователь;

Поэтому мы создаем новый объект пользователя и пытаемся найти подходящего пользователя. Если у нас его нет, мы его сохраняем. В любом случае мы вызываем обратный вызов done, который нам дал Passport, и передаем объект пользователя.

Напомним, что блок кода обрабатывает:

  • Настройка доступа к Facebook
  • Обработка результата

На самом деле вход в систему осуществляется через маршруты:

app.get ('/ auth / facebook', passport.authenticate ('facebook', {scope: "email"})); app.get ('/ auth / facebook / callback', passport.authenticate ('facebook', {successRedirect: '/', faultRedirect: '/ login'}));

Эти я взял прямо из документов, и, конечно, вы можете изменить их. Большая важная часть здесь - это область применения метода authenticate. Несмотря на то, что я настраиваю Facebook / Passport, чтобы получать электронную почту, я должен спрашивать ее конкретно при входе в систему. Вы должны сделать оба!

Мы не закончили. Последняя часть предназначена для обработки сериализации / десериализации объекта пользователя. По сути, мы пишем собственный код, чтобы сообщить Passport, как запомнить наш пользовательский объект, а затем как загрузить его обратно. Это будет зависеть от вашей системы упорства. Опять же, документы не очень ясно дают понять. Они используют подобный Монго код, фактически не сказав читателю, что демонстрируют один пример того, как он будет работать!

passport.serializeUser (function (user, done) {console.log (user); done (null, user._id);}); passport.deserializeUser (function (id, done) {user.findById (id, function (err, user) {done (err, user);});});

Этот код не специфичен для Facebook, он специфичен для Mongo / Mongoose.

И чтобы завершить, вам также нужен этот код для загрузки Passport и использования сессий:

app.use (passport.initialize ()); app.use (passport.session ());

Надеюсь, вы не потерялись!

Твиттер Войти

Хорошо, давай поговорим в твиттере. Для начала перейдите на https://apps.twitter.com и нажмите «Создать новое приложение» в правом верхнем углу.

Как и раньше, вам не нужно использовать «реальные» значения при тестировании, но Twitter немного придирчив. Описание должно содержать более 10 символов. Поле Сайт не должно быть локальным, но здесь вы можете использовать что угодно, даже CNN. URL моего обратного вызова был: http: // localhost: 3000 / auth / twitter / callback. Нажмите кнопку Создать.

Теперь нажмите на вкладку «Настройки». Здесь вы увидите два новых URL-адреса, которые раньше не существовали: «URL-адрес политики конфиденциальности» и «URL-адрес условий предоставления услуг». Через минуту мы скажем Twitter, что хотим получить адрес электронной почты людей, когда они аутентифицируются. Чтобы сделать это, вы должны указать здесь URL. Как и прежде, вы можете ввести тестовые URL-адреса или снова проверить CNN. Это не важно

Обязательно нажмите кнопку «Обновить настройки». Теперь нажмите «Ключи и токены доступа» на вкладках, а затем скопируйте ключ и секретный ключ пользователя.

Мы почти закончили. Twitter по умолчанию не позволит вам получить адрес электронной почты пользователя. Нажмите Permissions, и вы увидите флажок для запроса адреса электронной почты пользователя. (И обратите внимание, что здесь вы, вероятно, захотите изменить Доступ на «Только для чтения», если у вас нет планов по написанию подписчиков пользователя.)

Как я уже говорил, не забудьте нажать кнопку «Обновить настройки».

И ... теперь мы закончили. На стороне Твиттера. Давайте перейдем к коду, Бэтмен! Сначала добавим стратегию: npm install --save passport-twitter. Теперь в нашем главном файле приложения давайте добавим и настроим Twitter:

passport.use (new TwitterStrategy ({consumerKey: credentials.twitter.consumer_key, consumerSecret: credentials.twitter.consumer_secret, callbackURL: credentials.twitter.callback, includeEmail: true}, функция (токен, tokenSecret, профиль, выполнено) {var me = новый пользователь ({email: profile.emails [0] .value, имя: profile.displayName}); / * сохранить, если новый * / user.findOne ({email: me.email}, функция (err, u) { if (! u) {me.save (function (err, me) {if (err) return done done (err); done (null, me);});} else {console.log (u); done (null , и);}});}));

Вы заметите, что это практически то же самое. На самом деле мой обратный вызов на 100% одинаков. Я должен оптимизировать это, создав функцию, которую я могу вызывать из обеих стратегий. Если честно, когда я начал работать с этим, я не знал, что Passport сделает такую ​​хорошую работу с профилем. Ключевым моментом здесь является includeEmail: true. Я понятия не имею, где это задокументировано - я нашел это в отчете об ошибке, но это была половина работы, чтобы сообщить Твиттеру, что мне нужна электронная почта. Теперь давайте посмотрим на маршруты:

app.get ('/ auth / twitter', passport.authenticate ('twitter', {scope: ['include_email = true']})); app.get ('/ auth / twitter / callback', passport.authenticate ('twitter', {successRedirect: '/', faultRedirect: '/ login'}));

И здесь вы можете увидеть вторую часть - значение объема. Опять же - может быть, это задокументировано, но я смог запустить работу только путем поиска ошибок. Настоящая боль, ты знаешь что.

Но наконец - это все. Теперь я могу войти из любой сети, создать пользователя на основе моей электронной почты и поехать в город. Вот полный app.js для моего проекта.

var express = require ('express'); var exphbs = require ('express-handlebars'); var user = require ('./ models / user'); var credentials = require ('./ credentials.json'); var passport = require ('passport'), TwitterStrategy = require ('passport-twitter'). Strategy, FacebookStrategy = require ('passport-facebook'). Strategy; passport.use (new FacebookStrategy ({clientID: credentials.facebook.app_id, clientSecret: credentials.facebook.app_secret, callbackURL: credentials.facebook.callback, profileFields: ['id', 'displayName', 'emails']}, функция (accessToken, refreshToken, profile, done) {console.log (profile); var me = новый пользователь ({email: profile.emails [0] .value, name: profile.displayName}); / * сохранить, если новый * / user.findOne ({email: me.email}, function (err, u) {if (! u) {me.save (function (err, me) {if (err) return done done (err); done (null, me);});} else {console.log (u); done (null, u);}});})); passport.use (new TwitterStrategy ({consumerKey: credentials.twitter.consumer_key, consumerSecret: credentials.twitter.consumer_secret, callbackURL: credentials.twitter.callback, includeEmail: true}, функция (токен, tokenSecret, профиль, выполнено) {var me = новый пользователь ({email: profile.emails [0] .value, имя: profile.displayName}); / * сохранить, если новый * / user.findOne ({email: me.email}, функция (err, u) { if (! u) {me.save (function (err, me) {if (err) return done done (err); done (null, me);});} else {console.log (u); done (null , и);}});})); passport.serializeUser (function (user, done) {console.log (user); done (null, user._id);}); passport.deserializeUser (function (id, done) {user.findById (id, function (err, user) {done (err, user);});}); var app = express (); app.use (требуется ( 'куки-анализатор') (credentials.cookieSecret)); app.use (require ('express-session') ({resave: false, saveUninitialized: false, secret: credentials.cookieSecret})); app.use (passport.initialize ()); app.use (passport.session ()); app.engine ('handlebars', exphbs ({defaultLayout: 'main'})); app.set ('view engine', 'handlebars'); var mongoose = require ('mongoose'); var opts = {server: {socketOptions: {keepAlive: 1}}}; switch (app.get ('env')) {case 'development': mongoose.connect (credentials.mongo.development.connectionString, opts); перерыв; case 'production': mongoose.connect (credentials.mongo.production.connectionString, opts); перерыв; по умолчанию: выбрасывать новую ошибку ('Неизвестная среда выполнения:', app.get ('env')); } app.set ('port', process.env.PORT || 3000); app.use (express.static (__dirname + '/ public')); function isLoggedIn (req, res, next) {req.loggedIn = !! req.user; следующий(); } app.get ('/', isLoggedIn, function (req, res) {res.render ('index', {title: 'Welcome to Fool', loggedIn: req.loggedIn});}); app.get ('/ auth / facebook', passport.authenticate ('facebook', {scope: "email"})); app.get ('/ auth / facebook / callback', passport.authenticate ('facebook', {successRedirect: '/', faultRedirect: '/ login'})); app.get ('/ auth / twitter', passport.authenticate ('twitter', {scope: ['include_email = true']})); app.get ('/ auth / twitter / callback', passport.authenticate ('twitter', {successRedirect: '/', faultRedirect: '/ login'})); app.get ('/ login', isLoggedIn, function (req, res) {if (req.loggedIn) res.redirect ('/'); console.log (req.loggedIn); res.render ('login', {title: 'Логин / Регистрация'});}); // 500 обработчик ошибок (промежуточное программное обеспечение) app.use (function (err, req, res, next) {console.error (err.stack); res.status (500); res.render ('error');}) ; app.listen (app.get ('port'), function () {console.log ('Экспресс работает на http: // localhost:' + app.get ('port'));});Довольно просто, правда?
Зачем?