• BSA Lab.
      home

  • apps
    Главная
  • contacts
    Об авторе
  • library_books
    Блог
  • shop
    Портфолио
    keyboard_arrow_down
    • Google API
    • Gapi-People
      wc
    • Gapi-Email
      mail_outline
    • Gapi-Examples
      more

    • Silex (PHP micro-framework)
    • Silex-MVC
      desktop_windows
    • Silex-UBKI
      last_page

    • Zend (PHP framework)
    • ZF-MyBlog
      library_books
    • ZF2-ASM
      show_chart

    • Node.js
    • Express-Passport
      verified_user
    • Feathers-Examples
      more

    • Vue.js (JavaScript framework)
    • Vue-Resume
      account_circle
    • Vue-Business-Light
      work
    • Vue-Examples
      more
    • Vuex-Examples
      more

    • Nuxt (JavaScript framework)
    • Nuxt-Business-Light
      work
    • Nuxt-Vuetify-Start
      picture_in_picture

  • Темы
  • message
    WEB ресурсы
    keyboard_arrow_down
    • Обзор
      filter_none

  • message
    Google Client API
    keyboard_arrow_down
    • Обзор
      filter_none
    • Gmail-Send
      contact_mail
    • Gmail-Inbox
      mail_outline
BSA / Portfolio-node-express-passport
  • Информация

  • contact_mail
    Контакты
  • contacts
    Об авторе
  • public
    Мои проекты на GitHub
Passport for Node.js
Примеры аутентификации/авторизации пользователя.

2018-06-20

shop

Введение.

Этот проект был написан чтобы продемонстрировать пользовательскую аутентификацию и авторизацию с помощью различных методов. Эти методы были реализованы с помощью различных стратегий Passport.js. Таких как Local стратегия (использующая username и password). А также другие стратегии такие как Facebook, Twitter и многие другие.. При разработке своего проекта я использовал основные идеи из приложения Hackathon-starter.

Этот проект на GitHub можно посмотреть здесь

Демонстрацию этого проекта можно посмотреть здесь

Обзор

Простая, ненавязчивая аутентификация для Node.js — passportjs.org. PassportJS — это middleware для авторизации под node.js. Passport поддерживает авторизацию с помощью огромного количества сервисов, включая «ВКонтакте» и прочие твиттеры. Список сервисов можно просмотреть здесь.

Возможности

  • Local Authentication используя Email и Password
  • OAuth 1.0a Authentication через Twitter
  • OAuth 2.0 Authentication через Facebook, Google, GitHub, LinkedIn, Instagram
  • Flash сообщения
  • MVC структура приложения
  • Bootstrap 3 + jQuery
  • Контактная форма (для передачи почты используется сервис Sendgrid)
  • Управление Аккаунтами
  • Gravatar
  • Профайл Детали
  • Изменить Пароль
  • Забыли Пароль
  • Сбросить Пароль
  • Подключение мульти OAuth стратегий к одному аккаунту
  • Удалить Аккаунт
  • CSRF защита
  • API Примеры: Facebook, Foursquare, Last.fm, Tumblr, Twitter, Stripe, LinkedIn и т.д.

Ресурсы

Node.js (Chrome's V8 JavaScript engine)
Документация
Express (Node.js framework)
Документация
API
Passport.js
Документация
jQuery
Документация
Bootstrap 3
Документация
Font Awesome - the iconic font and CSS toolkit
Icons
MongoDB (noSQL DataBase)
Документация
Atlas (облачный хостинг)
mLab (облачный хостинг 500MB FREE)
Сompass (инструмент для визуализации данных)
Mongoosejs (NPM компонент для работы с моделями базы данных)
Интересные статьи
Authenticating Node.js Applications With Passport
Easy Node Authentication
How To Implement Password Reset In Node.js
Аутентификация в Node.js. Учебные руководства и возможные ошибки

Установка приложения

Предварительные требования

  1. Убедитесь в том, что у вас установлен NodeJS 8.0+ и MongoDB.

  2. Клонируйте или загрузите express-passport проект с GitHub.

  3. Инсталируйте ваши зависимости
    cd <project-name> npm install # Or yarn install

Переменные окружения

Файл .env должен находиться в корне проекта, если он отсутствует, то его нужно создать на основе примера файла .env.example, иначе приложение будет выдавать ошибку. Файл .env устанавливает переменные окружения. В переменных окружения обычно указываются секретные данные пользователя например user_id, user_secret и т.д.

Запуск вашего проекта

Development

npm run dev

Приложение будет запущено в режиме Разработки и будет выполнятся на http://localhost:3000

Production

npm start

Приложение будет запущено в Рабочем режиме и будет выполнятся на рабочем хостинге пример можно посмотреть здесь

Структура приложения

Файловая структура

пр.1
Имя файлаОписание
config/mongod.config.ymlКонфигурация для MongoDB.
config/passport.jsPassport Local и OAuth стратегии, плюс логин middleware.
controllers/api.jsКонтроллер для /api маршрутизации и всех api примеров.
controllers/contact.jsКотроллер для работы с формой контактов.
controllers/home.jsКонтроллер для домашней страницы (home).
controllers/index.jsПривязка контроллеров к маршрутам.
controllers/user.jsКонтроллер для управления учетными записями пользователей.
data/db/mongodПапка расположения базы данных MongoDB.
data/log/mongodПапка расположения логов для базы данных MongoDB.
data/uploadsПапка для загрузки файлов.
models/index.jsСоединение с базой данных MongoDB.
models/User.jsMongoose схема и модель для пользователя.
public/Папка для публичных данных (css, js, images).
views/account/Шаблон для изменения данных профайла, изменения пароля, удаления аккаунта, добавление ссылок на другие аккаунты..
views/api/Шаблон для тестирования API различных сервисов.
views/partials/flash.pugШаблон для отображения предупреждений об ошибках, информации и успешного завершения операции.
views/partials/header.pugШаблон для отображения верхнего меню.
views/partials/footer.pugШаблон для отображения нижнего колонтитула.
views/contact.pugШаблон формы контактов.
views/error.pugШаблон для отображения ошибок.
views/home.pugШаблон для отображения домашней страницы.
views/layout.pugШаблон основного макета.
.envВаши реальные API ключи, уникальные данные, пароли и URI базы данных.
.env.exampleПримеры API ключей, уникальных данных, паролей и URI базы данных.
.gitignoreПапки и файлы, которые будут игнорироваться системой управления версиями Git.
app.jsГлавный файл приложения.
package.jsonNPM зависимости.
package-lock.jsonСодержит точные версии зависимостей NPM в package.json.
server.jsЗапуск локального сервера.

Замечание: Здесь представлен мой вариант организации структуры приложения. Вы можете изменять структуру приложения по своему усмотрению...

Список NPM пакетов

пр.2
ПакетОписание
@octokit/restGitHub API library.
@sendgrid/mailMail library.
bcrypt-nodejsLibrary for hashing and salting user passwords.
body-parserNode.js body parsing middleware.
chalkTerminal string styling done right.
cheerioScrape web pages using jQuery-style syntax.
clockworkClockwork SMS API library.
compressionNode.js compression middleware.
cross-envSetting environment variables.
debugDebug library.
dotenvLoads environment variables from .env file.
expressNode.js web framework.
express-flashProvides flash messages for Express.
express-sessionSimple session middleware for Express.
express-status-monitorReports real-time server metrics for Express.
express-validatorEasy form validation for Express.
fbgraphFacebook Graph API library.
instagram-nodeInstagram API library.
lastfmLast.fm API library.
lobLob API library.
luscaCSRF middleware.
mongooseMongoDB ODM.
morganHTTP request logger middleware for node.js.
multerNode.js middleware for handling multipart/form-data.
node-foursquareFoursquare API library.
node-linkedinLinkedIn API library.
passportSimple and elegant authentication library for node.js.
passport-facebookSign-in with Facebook plugin.
passport-githubSign-in with GitHub plugin.
passport-google-oauthSign-in with Google plugin.
passport-instagramSign-in with Instagram plugin.
passport-linkedin-oauth2Sign-in with LinkedIn plugin.
passport-localSign-in with Username and Password plugin.
passport-openidSign-in with OpenId plugin.
passport-oauthAllows you to set up your own OAuth 1.0a and OAuth 2.0 strategies.
passport-twitterSign-in with Twitter plugin.
paypal-rest-sdkPayPal APIs library.
pug (jade)Template engine for Express.
requestSimplified HTTP request library.
stripeOffical Stripe API library.
tumblr.jsTumblr API library.
twilioTwilio API library.
twitTwitter API library.
validatorUsed in conjunction with express-validator in controllers/api.js.

Конфигурация

.env

Файл .env должен находиться в корне проекта, если он отсутствует, то его нужно создать на основе примера файла .env.example, иначе приложение будет выдавать ошибку. Файл .env устанавливает переменные окружения. В переменных окружения обычно указываются секретные данные пользователя например user_id, user_secret и т.д.

пр.3 для .env.example

    MY_EMAIL=my@mail.com

    BASE_URL=http://localhost:8080
    MONGODB_URI=mongodb://localhost:27017/test

    SESSION_SECRET=Your Session Secret goes here

    MAILGUN_USER=postmaster@sandbox697fcddc09814c6b83718b9fd5d4e5dc.mailgun.org
    MAILGUN_PASSWORD=29eldds1uri6

    SENDGRID_USER=hslogin
    SENDGRID_PASSWORD=hspassword00
    SENDGRID_API_KEY=SG.EV-XXXXXXXXXXXXXXXXXXX.XXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXX

    NYT_KEY=9548be6f3a64163d23e1539f067fcabd:5:68537648

    LASTFM_KEY=c8c0ea1c4a6b199b3429722512fbd17f
    LASTFM_SECRET=is cb7857b8fba83f819ea46ca13681fe71

    FACEBOOK_ID=754220301289665
    FACEBOOK_SECRET=41860e58c256a3d7ad8267d3c1939a4a

    INSTAGRAM_ID=9f5c39ab236a48e0aec354acb77eee9b
    INSTAGRAM_SECRET=5920619aafe842128673e793a1c40028

    GITHUB_ID=cb448b1d4f0c743a1e36
    GITHUB_SECRET=815aa4606f476444691c5f1c16b9c70da6714dc6

    TWITTER_KEY=6NNBDyJ2TavL407A3lWxPFKBI
    TWITTER_SECRET=ZHaYyK3DQCqv49Z9ofsYdqiUgeoICyh6uoBgFfu7OeYC7wTQKa

    GOOGLE_ID=828110519058.apps.googleusercontent.com
    GOOGLE_SECRET=JdZsIaWhUFIchmC1a_IZzOHb

    LINKEDIN_ID=77chexmowru601
    LINKEDIN_SECRET=szdC8lN2s2SuMSy8
    LINKEDIN_CALLBACK_URL=http://localhost:8080/auth/linkedin/callback

    STEAM_KEY=D1240DEF4D41D416FD291D0075B6ED3F

    TWILIO_SID=AC6f0edc4c47becc6d0a952536fc9a6025
    TWILIO_TOKEN=a67170ff7afa2df3f4c7d97cd240d0f3

    CLOCKWORK_KEY=9ffb267f88df55762f74ba2f517a66dc8bedac5a

    STRIPE_SKEY=sk_test_BQokikJOvBiI2HlWgH4olfQ2
    STRIPE_PKEY=pk_test_6pRNASCoBOKtIshFeQd4XMUh

    TUMBLR_KEY=FaXbGf5gkhswzDqSMYI42QCPYoHsu5MIDciAhTyYjehotQpJvM
    TUMBLR_SECRET=QpCTs5IMMCsCImwdvFiqyGtIZwowF5o3UXonjPoNp4HVtJAL4o

    FOURSQUARE_ID=2STROLSFBMZLAHG3IBA141EM2HGRF0IRIBB4KXMOGA2EH3JG
    FOURSQUARE_SECRET=UAABFAWTIHIUFBL0PDC3TDMSXJF2GTGWLD3BES1QHXKAIYQB
    FOURSQUARE_REDIRECT_URL=http://localhost:8080/auth/foursquare/callback

    PAYPAL_ID=AdGE8hDyixVoHmbhASqAThfbBcrbcgiJPBwlAM7u7Kfq3YU-iPGc6BXaTppt
    PAYPAL_SECRET=EPN0WxB5PaRaumTB1ZpCuuTqLqIlF6_EWUcAbZV99Eu86YeNBVm9KVsw_Ez5
    PAYPAL_RETURN_URL=http://localhost:8080/api/paypal/success
    PAYPAL_CANCEL_URL=http://localhost:8080/api/paypal/cancel

    LOB_KEY=test_814e892b199d65ef6dbb3e4ad24689559ca

    PINTEREST_ID=4819282851912494691
    PINTEREST_SECRET=b32f578ad83d94c058c6682329220feda7e5817e043a1cc4a5a1e28f51c70301
    PINTEREST_REDIRECT_URL=https://localhost:8080/auth/pinterest/callback

    GOOGLE_MAP_API_KEY=google-map-api-key

Замечание: Чтобы иметь реальные ключи для доступа к API различных сервисов нужно зарегистрировать ваше приложение в этих сервисах. Примеры регистрации приложения и получения ключей API в разных сервисах можно посмотреть здесь.

Работа приложения

  • В приложении я использую базу данных MongoDB. Можно установить MongoDB локально на компьютер или использовать MongoDB на облачном хостинге Atlas или mLab.
  • Чтобы тестировать в приложении возможность логирования с помощью таких сервисов как Facebook, Google, GitHub, LinkedIn, Instagram и т.д. нужно быть там зарегистрированным и иметь свой аккаунт.
  • Если вы разработчик и хотите создавать приложения с возможностью логирования и доступа других пользователей к API различных сервисов, нужно зарегистрировать ваше приложение в этих сервисах. Примеры регистрации приложения и получения ключей API в разных сервисах можно посмотреть здесь
  • Для передачи почтовых сообщений я пользуюсь сервисом SendGrid. Этот сервис дает возможность передавать бесплатно до 100 сообщений в день, но для этого нужно зарегистрироваться в нем и получить SENDGRID_API_KEY.

    Замечание: Для безопасности этот сервис предлагает включать в свой белый список те IP адреса с которых вы хотите передавать сообщения. Важно, чтобы ваш IP был постоянен, иначе передача сообщений будет не возможна...

  • Для построения пользовательского интерфейса используется Bootstrap 3, это один из популярных HTML, CSS, и JS фреймворков для разработки интерактивных мобильных проектов в Интернете.

Mongoose модель для аккаунтов пользователей

Аккаунты зарегистрированных пользователей храняться в базе данных MongoDB в таблице Users. Структрура таблицы задается схемой userSchema в модуле models/User.js см. пр.4

пр.4

 const userSchema = new mongoose.Schema({
    email: {type: String, unique: true},
    password: String,
    passwordResetToken: String,
    passwordResetExpires: Date,

    facebook: String,
    twitter: String,
    google: String,
    github: String,
    instagram: String,
    linkedin: String,
    steam: String,
    tokens: Array,

    profile: {
        name: String,
        gender: String,
        location: String,
        website: String,
        picture: String
    }
}, {timestamps: true});

Из структуры модели models/User.js видно, что каждый аккаунт пользователя должен иметь обязательное уникальное поле адреса Email.

Создать аккаунт пользователя можно двумя способами:

  • Через форму Create Account например по адресу. В форме вы вводите адрес электронной почты и ваш пароль и если ваш email будет уникальным, то в базе создается запись с вашим аккаунтом с полями email и password
  • Через форму Login например по адресу. В форме из списка вы выбираете сервис (Facebook, Google, LinkedIn...) и если этот сервис не присоединен к вашему уже существующему аккаунту и в этом сервисе, указанный email отличается от других электронных адресов, существующих аккаунтов, тогда создается новый аккаунт с электронным адресом этого сервиса и полем с названием этого сервиса со значением его id например instagram: "2000082009", email: "my@instagram.com".

    При этом также из этого сервиса регистрируемый пользователь получаем данные для своего профайла в поле profile и получает ключи доступа к своему API в поле tokens.

Также в структуре модели мы видим два поля passwordResetToken: String, passwordResetExpires: Date, которые используются при алгоритме восстановления пароля, когда пользователь забывает свой пароль. а параметр {timestamps: true} указывает модели чтобы она создавала поля даты создания и изменения аккаунта например createdAt: 2018-06-19 11:18:12.867, updatedAt: 2018-06-23 08:39:29.442.

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

Замечание: Присоединить к текущему аккаунту можно только такой сервис, который еще не был присоединен к нашим другим аккаунтам, т.е. выбранный сервис может быть присоединен к аккаунту только один раз.

При присоединении сервиса к аккаунту появляется поле этого сервиса с его id, например google: "110341449488589699610", и в поле tokens получаем ключи доступа к API этого сервиса, например tokens:[{kind: "google", accessToken: "ya29.xxx..."}] А также из этого сервиса получаем данные профайла в поле profile значения которых были пустыми.

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

Иногда возникает необходимость для некоторого сервиса просто сохранить коды доступа к его API в поле tokens в текущем аккаунте, при этом этот сервис не учавствует в аутентификации пользователя а используется только для доступа к своим ресурсам через свои tokens. Такой подход применяется для некоторых примеров API в модуле controllers/api.js.

Стратегии Passport.js

Существует множество стратегий для Passport.js которые можно посмотреть здесь. В нашем проекте стратегии реализованы в модуле config/pasport.js. Рассмотрим основные из них.

Local

Стратегия Local использует email и password при аутентификации пользователя.

пр.5


/**
 * Sign in using Email and Password.
 */
passport.use('local', new LocalStrategy({usernameField: 'email'}, (email, password, done) => {
    User.findOne({email: email.toLowerCase()}, (err, user) => {
        if (err) {
            return done(err);
        }
        if (!user) {
            return done(null, false, {msg: `Email ${email} not found.`});
        }
        user.comparePassword(password, (err, isMatch) => {
            if (err) {
                return done(err);
            }
            if (isMatch) {
                return done(null, user);
            }
            return done(null, false, {msg: 'Invalid email or password.'});
        });
    });
}));
    
Логика такая: пользователь передает свой email и password стратегия ищет аккаунт по переданному email, если не находит - выдает ошибку Email not found.. Если находит, то сравнивает полученный password с существующим, если password отличается - выдает ошибку Invalid email or password. Если password совпадает, то выдается положительный результат и происходит аутентификация пользователя.

GitHub

Стратегия GitHub и другие стратегии которые используются при аутентификации и авторизации пользователя.

пр.6


/**
 * OAuth Strategy Overview
 *
 * - User is already logged in.
 *   - Check if there is an existing account with a provider id.
 *     - If there is, return an error message. (Account merging not supported)
 *     - Else link new OAuth account with currently logged-in user.
 * - User is not logged in.
 *   - Check if it's a returning user.
 *     - If returning user, sign in and we are done.
 *     - Else check if there is an existing account with user's email.
 *       - If there is, return an error message.
 *       - Else create a new account.
 */

/**
 * Sign in with GitHub.
 */
passport.use('github', new GitHubStrategy({
    clientID: process.env.GITHUB_ID,
    clientSecret: process.env.GITHUB_SECRET,
    callbackURL: '/auth/github/callback',
    passReqToCallback: true
}, (req, accessToken, refreshToken, profile, done) => {
    if (req.user) {
        User.findOne({github: profile.id}, (err, existingUser) => {
            if (existingUser) {
                req.flash('errors', {msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.'});
                done(err);
            } else {
                User.findById(req.user.id, (err, user) => {
                    if (err) {
                        return done(err);
                    }
                    user.github = profile.id;
                    user.tokens.push({kind: 'github', accessToken});
                    user.profile.name = user.profile.name || profile.displayName;
                    user.profile.picture = user.profile.picture || profile._json.avatar_url;
                    user.profile.location = user.profile.location || profile._json.location;
                    user.profile.website = user.profile.website || profile._json.blog;
                    user.save((err) => {
                        req.flash('info', {msg: 'GitHub account has been linked.'});
                        done(err, user);
                    });
                });
            }
        });
    } else {
        User.findOne({github: profile.id}, (err, existingUser) => {
            if (err) {
                return done(err);
            }
            if (existingUser) {
                return done(null, existingUser);
            }
            User.findOne({email: profile._json.email}, (err, existingEmailUser) => {
                if (err) {
                    return done(err);
                }
                if (existingEmailUser) {
                    req.flash('errors', {msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.'});
                    done(err);
                } else {
                    const user = new User();
                    user.email = profile._json.email;
                    user.github = profile.id;
                    user.tokens.push({kind: 'github', accessToken});
                    user.profile.name = profile.displayName;
                    user.profile.picture = profile._json.avatar_url;
                    user.profile.location = profile._json.location;
                    user.profile.website = profile._json.blog;
                    user.save((err) => {
                        done(err, user);
                    });
                }
            });
        });
    }
}));
    
Логика такая: пользователь аутентифицируется на сервисе GitHub если аутентификация прошла успшно, то получает от сервиса (accessToken, refreshToken, profile).

Если пользователь уже прошел аутентификацию на сайте, то по полю github: profile.id в базе данных ищется соответствующий аккаунт пользователя. Если аккаунт с таким пользователем найден, то выдается ошибка There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account. Это значит, что такой сервис может идентифицироваться только с одним аккаунтом. Если аккаунт не найден, то в него добавляется поле github = profile.id, в поле tokens добавляется обьект {kind: 'github', accessToken}, а в поле profile добавляются отсутствующие значения полей name, picture, location, website.

Если пользователь не прошел аутентификацию на сайте, то по полю github: profile.id в базе данных ищется соответствующий аккаунт пользователя. Если аккаунт с таким пользователем найден, то выдается положительный результат и происходит аутентификация пользователя на сайте. Если аккаунт с таким пользователем не найден, то по полю email: profile._json.email в базе данных ищется соответствующий аккаунт пользователя. Если аккаунт с email найден, то выдается ошибка There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.. Это значит, что значение email должно быть уникальным для всех зарегистрированных аккаунтах. Если аккаунт с email не найден, то создается новый аккаунт с уникальным полем email = profile._json.email в аккаунт добавляется поле github = profile.id, в поле tokens аккаунта добавляется обьект {kind: 'github', accessToken}, а в поле profile аккаунта добавляются значения полей name, picture, location, website.

OAuth

Стратегия OAuth которя используются только при авторизации пользователя.

пр.7


/**
 * Tumblr API OAuth.
 */
passport.use('tumblr', new OAuthStrategy({
    requestTokenURL: 'https://www.tumblr.com/oauth/request_token',
    accessTokenURL: 'https://www.tumblr.com/oauth/access_token',
    userAuthorizationURL: 'https://www.tumblr.com/oauth/authorize',
    consumerKey: process.env.TUMBLR_KEY,
    consumerSecret: process.env.TUMBLR_SECRET,
    callbackURL: '/auth/tumblr/callback',
    passReqToCallback: true
}, (req, token, tokenSecret, profile, done) => {
    User.findById(req.user._id, (err, user) => {
        if (err) {
            return done(err);
        }
        user.tokens.push({kind: 'tumblr', accessToken: token, tokenSecret});
        user.save((err) => {
            done(err, user);
        });
    });
}));
    
Логика такая: стратегия используется чтобы сохранить ключи доступа (accessToken, tokenSecret) к API соответствующего сервиса в текущем аккаунте, в поле tokens аккаунта добавляется обьект {kind: 'tumblr', accessToken: token, tokenSecret}.

OAuth2

Стратегия OAuth2 которя используются только при авторизации пользователя.

пр.8


/**
 * Pinterest API OAuth2.
 */
passport.use('pinterest', new OAuth2Strategy({
    authorizationURL: 'https://api.pinterest.com/oauth/',
    tokenURL: 'https://api.pinterest.com/v1/oauth/token',
    clientID: process.env.PINTEREST_ID,
    clientSecret: process.env.PINTEREST_SECRET,
    callbackURL: process.env.PINTEREST_REDIRECT_URL,
    passReqToCallback: true
}, (req, accessToken, refreshToken, profile, done) => {
    User.findById(req.user._id, (err, user) => {
        if (err) {
            return done(err);
        }
        user.tokens.push({kind: 'pinterest', accessToken});
        user.save((err) => {
            done(err, user);
        });
    });
}));
    
Логика такая: стратегия используется чтобы сохранить ключи доступа (accessToken) к API соответствующего сервиса в текущем аккаунте, в поле tokens аккаунта добавляется обьект {kind: 'pinterest', accessToken}.

Gravatar ( globally recognized avatar — глобально распознаваемый аватар)

Это Web 2.0 сервис, позволяющий интернет-пользователям хранить свой аватар на специальном сервере. Пользователь регистрируется на центральном сервере и сохраняет там свой аватар и адрес электронной почты. Когда он оставляет комментарий на сайте или блоге, поддерживающем Gravatar, и указывает свой адрес электронной почты, на стороне сайта вычисляется MD5-хэш от почтового адреса и отправляется на сервер Gravatar, в ответ возвращается аватар пользователя. Таким образом система Gravatar позволяет использовать аватары без регистрации на сайте или блоге.

В приложении эта возможность реализована как метод модели User в модуле models/User.js см. пр.9

пр.9

/**
 * Helper method for getting user's gravatar.
 */
userSchema.methods.gravatar = function gravatar(size) {
    if (!size) {
        size = 200;
    }
    if (!this.email) {
        return `https://gravatar.com/avatar/?s=${size}&d=retro`;
    }
    const md5 = crypto.createHash('md5').update(this.email).digest('hex');
    return `https://secure.gravatar.com/avatar/${md5}?s=${size}&d=retro`;
};
    

Передача Email

Для передачи почтовых сообщений я пользуюсь сервисом SendGrid. Этот сервис дает возможность передавать бесплатно до 100 сообщений в день, но для этого нужно зарегистрироваться в нем и получить SENDGRID_API_KEY.

Замечание: Для безопасности этот сервис предлагает включать в свой белый список те IP адреса с которых вы хотите передавать сообщения. Важно, чтобы ваш IP был постоянен, иначе передача сообщений будет не возможна...

В приложении эта возможность реализована как метод контроллера postContact в модуле controllers/contact.js см. пр.10

пр.10

/**
 * POST /contact
 * Send a contact form via SendGrid.
 */
exports.postContact = (req, res) => {
    let fromName;
    let fromEmail;
    if (!req.user) {
        req.assert('name', 'Name cannot be blank').notEmpty();
        req.assert('email', 'Email is not valid').isEmail();
    }
    req.assert('message', 'Message cannot be blank').notEmpty();

    const errors = req.validationErrors();

    if (errors) {
        req.flash('errors', errors);
        return res.redirect('/contact');
    }

    if (!req.user) {
        fromName = req.body.name;
        fromEmail = req.body.email;
    } else {
        fromName = req.user.profile.name || '';
        fromEmail = req.user.email;
        if(!fromEmail){
            req.flash('errors', {msg: 'Field fromEmail cannot be blank. Enter the email address in your profile.'});
            return res.redirect('/contact');
        }
    }

    const mailOptions = {
        to: myEmail,
        from: `${fromName} <${fromEmail}>`,
        subject: 'Contact Form | Hackathon Starter',
        text: req.body.message
    };

    sgMail.send(mailOptions);
    req.flash('success', {msg: 'Email has been sent successfully!'});
    res.redirect('/contact');
};
    
© 2017 Сергей Бескоровайный
language Разработчик:
BSA Lab.