Senior Frontend Developer / Team Lead
0,0
рейтинг
7 ноября 2013 в 14:13

Разработка → Как работает Passport.js tutorial


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

Для самых нетерпеливых — готовый проект можно посмотреть здесь.

Зависимости:


  • «express»: «3.3.7»,
  • «passport»: "~0.1.17",
  • «passport-local»: "~0.1.6",
  • «mongoose»: "~3.8.0",


Так же я буду использовать для удобства несколько дополнительных утилит. Без них вполне можно обойтись:

  • «jade»: "*"
  • «bootable»: "~0.1.0"
  • «nconf»: "~0.6.8"
  • «require-tree»: "~0.3.2"
  • «winston»: "~0.7.2"


Модель пользователя


Для начала, я думаю, можно создать модель пользователя:
var UserSchema = new mongoose.Schema({
  username: {
    type: String,
    unique: true,
    required: true
  },
  password: {
    type: String,
    required: true
  },
});
mongoose.model('user', UserSchema);


Здесь можно было бы посолить пароль, и добавить немного магии безопасности для бога безопасности, но я оставляю это на вас =).

Стратегия авторизации



Верификация

Подключим и настроим стратегию авторизации.
var passport       = require('passport');
var LocalStrategy  = require('passport-local').Strategy;

passport.use(new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password'
}, function(username, password,done){
  User.findOne({ username : username},function(err,user){
    return err 
      ? done(err)
      : user
        ? password === user.password
          ? done(null, user)
          : done(null, false, { message: 'Incorrect password.' })
        : done(null, false, { message: 'Incorrect username.' });
  });
}));


LocalStrategy принимает 2 параметра: объект с опциями и middleware для верификации пользователя.
По-умолчанию, если в `LocalStrategy` не передавать никаких опций — стратегия будет искать параметры для авторизации пользователя в формах с именами `username` и `password`. При желании, можно указать свои имена форм, как я, собственно, и сделал.
Второй аргумент — middleware — принимает параметры `username`, `passport` и `done`. В done, вторым аргументом, передаем объект пользователя, если такой есть.

Привязка авторизации к пользователю

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

Каждый последующй запрос будет содержать cookies, с помощью которого passport сможет опознать пользователя, и достать его данные из сессии. Для того, чтобы сохранять или доставать пользовательские данные из сессии, паспорт использует функции `passport.serializeUser()` и `passport.deserializeUser()`.

passport.serializeUser(function(user, done) {
  done(null, user.id);
});


passport.deserializeUser(function(id, done) {
  User.findById(id, function(err,user){
    err 
      ? done(err)
      : done(null,user);
  });
});


Подключение Passport к Express


Окей, с этим разобрались, теперь нужно подключить Passport к Express:

// Middlewares, которые должны быть определены до passport:
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'SECRET' }));
 
// Passport:
app.use(passport.initialize());
app.use(passport.session());


Создание роутера и контроллеров


Настало время настройки роутера. Привяжем запросы к соответствующим контроллерам:

// Auth system
app.post('/login',                  controllers.users.login);
app.post('/register',               controllers.users.register);
app.get('/logout',                  controllers.users.logout);


Теперь создадем сами контроллеры:

// Здесь мы проверяем, передаем данные о пользователе в функцию верификации, котоую мы определили выше. 
// Вообще, passport.authenticate() вызывает метод req.logIn автоматически, здесь же я указал это явно. Это добавляет удобство в отладке. Например, можно вставить сюда console.log(), чтобы посмотреть, что происходит...
// При удачной авторизации данные пользователя будут храниться в req.user
module.exports.login = function(req, res, next) {
  passport.authenticate('local',
    function(err, user, info) {
      return err 
        ? next(err)
        : user
          ? req.logIn(user, function(err) {
              return err
                ? next(err)
                : res.redirect('/private');
            })
          : res.redirect('/');
    }
  )(req, res, next);
};

// Здесь все просто =)
module.exports.logout = function(req, res) {
  req.logout();
  res.redirect('/');
};

// Регистрация пользователя. Создаем его в базе данных, и тут же, после сохранения, вызываем метод `req.logIn`, авторизуя пользователя
module.exports.register = function(req, res, next) {
  var user = new User({ username: req.body.email, password: req.body.password});
  user.save(function(err) {
    return err
      ? next(err)
      : req.logIn(user, function(err) {
        return err
          ? next(err)
          : res.redirect('/private');
      });
  });
};


Проверка авторизации


Проверку авторизации можно делать с помощью req.isAuthenticated(). Я вынесу проверку в middleware.

exports.mustAuthenticatedMw = function (req, res, next){
  req.isAuthenticated()
    ? next()
    : res.redirect('/');
};


И добавлю в routes.

  App.all('private', mustAuthenticatedMw);
  App.all('private/*', mustAuthenticatedMw);


HTML


Осталось создать форму регистрации, авторизации, и кнопку логаута.

Регистрация:
h4 Register your account
form(action='/register' method='post')
  fieldset
    label(for='email') Email
    input(type='email' name='email' placeholder='Your Email')
    label(for='password') Password
    input(type='password' name='password' placeholder='Your Password')
    button(type='Register')


Логин
h4 Login to your account
form(action='/login' method='post')
  fieldset
    label(for='email') Email
    input(type='email' name='email' placeholder='Your Email')
    label(for='password') Password
    input(type='password' name='password' placeholder='Your Password')
    button(type='Login')


Логаут
a(href='/logout') logout


Чтож, можно запускать и проверять работу. Чтобы не переписывать сотни кода можно форкнуть репозиторий на GitHub.
Антон Шувалов @asheee
карма
50,0
рейтинг 0,0
Senior Frontend Developer / Team Lead
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (16)

  • 0
    Не совсем понял из статьи один момент — при регистрации из соц. сетей мы можем получить и e-mail пользователя?
    • 0
      Да.
      • 0
        И у ВК?
        • 0
          ВК не отдаёт email
          • 0
            Я про то же
    • 0
      В зависимости от соц. сети, из Twitter — нет, из Google — да.
    • 0
      Вконтакте — нет, только большим партнерам отдает
      Одноклассники — нет
      Фейсбук — да (надо в настройках приложения прописать)
      Gmail — да
  • 0
    Спасибо за require-tree.
  • –2
    Это все было бы еще лучше, если бы использовались futures. github.com/kriskowal/q
    • 0
      Код на гитхабе, все в ваших руках.
  • 0
    Удобная вещь, пользуемся ей в своём проекте, но ей не хватает унификации отдаваемых данных для разных стратегий. Например для фейсбука, линкедина и гугла аватар нужно искать в разных переменных, да и хотелось бы какой-то абстракции от всех этих API когда этот аватар просишь.
  • 0
    Подскажите пожалуйста, то ли лыжи не едут, то ли я дурак и не вижу очевидного)

    Делаю авторизацию с помощью passport-vkontakte для ВК. Локально все отрабатывает отлично. В частности callback выставлен что-то вроде http://localhost:1337/auth/vkontakte/callback. В настройках приложения ВК тоже выставил http://localhost:1337.

    Но при переносе на amazon сломалось, причем довольно странно. Я сменил callback и site address в ВК на http://my-amazon-domain.tk:1337/auth/vkontakte/callback, все по идее то должно было бы заработать, но получаю exception
    VkontakteAPIError: Validation required: please open redirect_uri in browser
    При попытке сделать попрошенное open redirect_uri in browser валится такое TokenError: Code is invalid or expired., что в принципе и не странно.

    Может кто сталкивался, и решение на поверхности?
    • 0
      Сервера у черта в амазонии. Надо ввести каптчу :)
      Вроде как.
  • 0
    Как понимаю, passport.js нужен только для серверных MVC-приложений. Если же у нас синглпейдж, то все настройки для соц. сетей задаются на клиенте?
    • 0
      Так и есть, да

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