How Passport.js Works

  • Tutorial

PassportJS is middleware for authorization under node.js. Passport supports authorization through a huge number of services, including VKontakte and other twitter. The list of services can be viewed here . I want to talk a little about how this middleware works on the example of the most ordinary authorization using login and password.

For the most impatient - the finished project can be viewed here .

Dependencies:


  • "Express": "3.3.7",
  • "Passport": "~ 0.1.17",
  • "Passport-local": "~ 0.1.6",
  • "Mongoose": "~ 3.8.0",


I will also use several additional utilities for convenience. It is quite possible to do without them:

  • "Jade": "*"
  • "Bootable": "~ 0.1.0"
  • "Nconf": "~ 0.6.8"
  • "Require-tree": "~ 0.3.2"
  • "Winston": "~ 0.7.2"


User model


To start, I think you can create a user model:
var UserSchema = new mongoose.Schema({
  username: {
    type: String,
    unique: true,
    required: true
  },
  password: {
    type: String,
    required: true
  },
});
mongoose.model('user', UserSchema);


Here it would be possible to salt the password, and add some security magic for the security god, but I leave it to you =).

Authorization Strategy



Verification

We will connect and configure the authorization strategy.
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 accepts 2 parameters: an object with options and middleware for user verification.
By default, if no options are passed in `LocalStrategy`, the strategy will look for parameters for authorizing the user in forms with the names` username` and `password`. If desired, you can specify your form names, as I actually did.
The second argument - middleware - takes the parameters `username`,` passport` and `done`. In done, the second argument is the user object, if any.

Binding authorization to user

In a typical web application, the credentials used to authenticate the user will only be transmitted during authorization. If everything is in order and the user exists, then information about him is stored in the session, and the session identifier, in turn, is stored in the browser cookies.

Each subsequent request will contain cookies, with which passport will be able to identify the user, and get his data from the session. In order to save or retrieve user data from the session, the passport uses the functions `passport.serializeUser ()` and `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);
  });
});


Connect Passport to Express


Ok, we figured it out, now you need to connect Passport to 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());


Creating a router and controllers


The time has come to configure the router. Bind the queries to the corresponding controllers:

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


Now create the controllers themselves:

// Здесь мы проверяем, передаем данные о пользователе в функцию верификации, котоую мы определили выше. 
// Вообще, 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');
      });
  });
};


Authorization Check


Authorization verification can be done using req.isAuthenticated (). I will check in middleware.

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


And add to the routes.

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


HTML


It remains to create a registration form, authorization, and a logout button.

Registration:
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')


Login
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')


Logout
a(href='/logout') logout


Well, you can run and check the work. In order not to rewrite hundreds of code, you can fork the repository on GitHub .

Also popular now: