Carlos
Carlos

Reputation: 133

How to keep a user logged on? Is it possible with ExpressJS and Passport?

I have a server in node.js using express and passport with the passport-local strategy.

I have the users in the database and through passport I'm able to authenticate them, unfortunately when a second request comes from the same client the req.isAuthenticated() method returns false. There is also no user in the request (req.user = undefined).

I've also checked and when doing the authentication although I get back a user from passport.authenticate('local'... I do not get req.user populated then. If I try to set it up manually it just doesn't propagate for following requests.

I don't understand what I'm doing wrong, here is my code.

server.js

var express = require('express'),
compass = require('node-compass'),
routes = require('./server/routes')
http = require('http'),
path = require('path'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
Database = require('./server/repositories/database'),
Configuration = require('./server/config').Config,
crypto = require('crypto');

var app = express();

app.enable("jsonp callback");

passport.use(new LocalStrategy(
  function(email, password, done) {
    process.nextTick(function () {
      var userService = new UserService();
      userService.login(email, crypto.createHash('md5').update(password).digest("hex"), function(error, user) {
        if (error) done(error, user);
        else if (!user) return done(null, false, { message: 'wrong credentials'}); 
        return done(null, user);
      });
    });
  }
));

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

passport.deserializeUser(function(id, done) {
  var userService = new UserService();
  userService.findById(id, function(err, user) {
    done(err, user);
  });
});

app.configure(function(){
  app.set('port', Configuration.Port);
  app.set('views', __dirname + '/app/views');
  app.set('view engine', 'ejs');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.cookieParser()); 
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(compass({
    project: path.join(__dirname, 'app'),
      sass: 'styles'
  }));
  app.use(express.session({ secret: 'keyboard cat' }));
  app.use(function(err, req, res, next){
    console.error(err.stack);
    res.send(500, 'Something broke!');
  });
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'app')));
});

routes.configure(app);

Database.open(function() { 
  app.listen(Configuration.Port, function() {
    console.log("Express server listening on port " + Configuration.Port);
  });
});

routes.js

var Configuration = require('./config').Config;
var ApiResult = require('../model/apiResult').ApiResult;
var ApiErrorResult = require('../model/apiErrorResult').ApiErrorResult;
var ApiReturnCodes = require('../model/apiReturnCodes').ApiReturnCodes;
var passport = require('passport');

var usersController = require('./controllers/usersController');

exports.configure = function(app) {
  function ensureAuthenticated(req, res, next) {
    console.log(req.isAuthenticated());
    if (req.isAuthenticated()) { return next(); }
    else {res.send(new ApiErrorResult(ApiReturnCodes.NOT_LOGGED_IN, null));}
  }

  app.post('/login', function(req, res, next) {
    passport.authenticate('local', function(err, user, info) {
      if (err || !user) { console.log(info); res.send(new ApiErrorResult(ApiReturnCodes.ENTITY_NOT_FOUND, null)); }
      // If this function gets called, authentication was successful.
      // `req.user` contains the authenticated user
      else res.send(new ApiResult(user));
    })(req,res,next);
  });

  app.get('/anotherLink', ensureAuthenticated, function(req, res, next) {
    res.json({Code:0});
  });
}

When I hit the link /anotherLink after being authenticated I get res.isAuthenticated() as false.

Also when I see the req.session after the ensureAuthenticated is called I get:

{ cookie: 
   { path: '/',
     _expires: null,
     originalMaxAge: null,
     httpOnly: true },
  passport: {} }

What am I missing for it to save the information that that user is authenticated? On the client side I'm using Angular only doing a simple get with the url without parameters.

If I forgot to put something here just tell me, I'll update it. Any help will be greatly appreciated. Thanks

Upvotes: 5

Views: 4891

Answers (1)

Carlos
Carlos

Reputation: 133

So I found out what was wrong with my code.

My passport.deserializeUser method used the method userService.findById

And that called the repository... like this:

userRepository.findUnique({"_id": id}, callback);

because the id was generated by MongoDB the correct call needs to be:

userRepository.findUnique({"_id": new ObjectID(id)}, callback);

I hope this saves some time to the next person with the same problem.

With this detail, this code should work nicely for everyone wanting to use the LocalStrategy on the Passport framework.

Upvotes: 7

Related Questions