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();
});
}
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 inthis.user
like so:Cheers :)