Node JS + Passport login

303 Views Asked by At

I'm trying to configure my first node js app to login using passport.

So first things first. I create a /app/config/express.js file to configure express stuff. So my app.js is simpler:

var app = require('./app/config/express')();

app.listen(3001, function(){
    console.log("Servidor rodando");
});

Ok... that's cool. BUT! My express.js file is too big. Maybe you can give me some tips on how to refactor this?

I added some comments with some questions that I would love to make this code better.

var express = require('express');
var load = require('express-load');
var expressValidator = require('express-validator');
var bodyParser = require('body-parser');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
var session = require('express-session');
var flash = require("connect-flash");

module.exports = function() {

    // PLEASE READ 1
    //
    // 
    // Functions to organize code better.
    // PS: if this functions are outside "module.exports", then Express
    // doesnt 'inject' the 'app.infra' variable .... 
    // Is there a workaround to move these functions outta here?
    //
    // 
    function configureAuth(){
        passport.use(new Strategy({
            passReqToCallback : true
          },
          function(req, username, password, cb) {

            var connection = app.infra.connectionFactory();
            var userDao = new app.infra.dao.UserDao(connection);

            userDao.login(username, password, function(err, user){
              if (err) {
                return cb(err);
              }

              if (!user) {
                return cb(null, false);
              }


              return cb(null, user);
            });

            connection.end();

          }));

        //
        // 
        // HERE IT IS!
        //
        //
        passport.serializeUser(function(user, cb) {
          cb(null, user.id);
        });

        passport.deserializeUser(function(id, cb) {
            cb(null, user);
        });
    }

    function configureExpressLibs(app){
        app.set('view engine', 'ejs');
        app.set('views','./app/views');

        app.use('/static', express.static('./app/public'));
        app.use(flash());

        app.use(bodyParser.urlencoded({extended: true}));
        app.use(bodyParser.json());
        app.use(expressValidator());

        app.use(session({
            secret: '086this 54is 23unkowned 67',
            resave: false,
            saveUninitialized: false
        }));

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

    function configureErrors(app){
        app.use(function(err, req, res, next) {
            console.error(err.stack)
            next(err)
        });

        app.use(function(req,res,next){
            res.status(404).render('errors/404');
            next();
        });


        app.use(function(error, req,res,next){
            res.status(500).render('errors/500');
            next();
        });
    }

     // PLEASE READ 2
     // 
     // 
     //  I've moved this to 'LoginController.js' in my routes folder but
     //  I didnt work... So I moved it here. Is there a work around?
     //
     //
    function configureLoginRoutes(app){

      function redirectToIndexIfLoggedIn(req, res, next) {
        if (req.isAuthenticated())
          res.redirect('/');

        return next();
      }

      app.get('/login', redirectToIndexIfLoggedIn, function(req, res){
        res.render('login/login');
      });

      app.post('/login', passport.authenticate('local', {
        successRedirect : '/',
        failureRedirect : '/login',
        failureFlash : 'Invalid username or password.'
        }));

      app.get('/logout', function(req, res){
          req.logout();
          req.session.destroy();
          res.redirect('/');
      });
    }


    var app = express();

    configureExpressLibs(app);

    configureAuth();

    configureLoginRoutes(app);


    load('routes',{cwd: 'app'})
        .then('infra')
        .into(app);

    configureErrors(app);

    return app;
}

So the problem now is, when I login (it doesnt matter if the user is correct or wrong), I get a:

Error: Failed to serialize user into session

I googled it and saw the the reason for this is because people forgot to implement "serializeUser". But I did. Please check the comment with "HERE IT IS" on the code above.

Thanks guys. Sorry for the big code. But I'm learning and I hope to make things better with your help.

EDIT My deserialize method was wrong. I fixed it using:

    passport.deserializeUser(function(id, cb) {
      var connection = app.infra.connectionFactory();
      var userDao = new app.infra.dao.UserDao(connection);

      userDao.findById(id, function(err, user) {
        done(err, user);
      });

      connection.end();

    });

but the application still fails. Same error.

EDIT SOLUTION

Turns out my implementation was wrong. You see, mysql always returns an array. Thus I corrected my code like this:

      function(req, username, password, cb) {

        var connection = app.infra.connectionFactory();
        var userDao = new app.infra.dao.UserDao(connection);

        userDao.login(username, password, function(err, user){
          if (err) {
            return cb(err);
          }

          if (!user) {
            return cb(null, false);
          }

          // HERE

          return cb(null, user[0]);
        });

        connection.end();

      }));

And here:

    passport.deserializeUser(function(id, cb) {
      var connection = app.infra.connectionFactory();
      var userDao = new app.infra.dao.UserDao(connection);

      userDao.findById(id, function(err, user) {
         // HERE
         cb(err, user[0]);
      });

      connection.end();

    });
} 
1

There are 1 best solutions below

2
On

I think your this.user is not setting inside deserializeUser when you call cb(null,user) so create your own middleware after app.use(passport.session()) to put it in this.user like so:

app.use(function * setUserInContext (next) {
  this.user = this.req.user
  yield next
})

Cheers :)