217

I got a problem with the Passport.js module and Express.js.

This is my code and I just want to use a hardcoded login for the first try.

I always get the message:

I searched a lot and found some posts in stackoverflow but I didnt get the failure.

Error: failed to serialize user into session
    at pass (c:\Development\private\aortmann\bootstrap_blog\node_modules\passport\lib\passport\index.js:275:19)

My code looks like this.

'use strict';

var express = require('express');
var path = require('path');
var fs = require('fs');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var nodemailer = require('nodemailer');

var app = express();

module.exports = function setupBlog(mailTransport, database){
var config = JSON.parse(fs.readFileSync('./blog.config'));

app.set('view options', {layout: false});

app.use(express.static(path.join(__dirname, '../', 'resources', 'html')));


app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());


app.get('/blog/:blogTitle', function(req, res) {
  var blogTitle = req.params.blogTitle;
  if(blogTitle === 'newest'){
    database.getLatestBlogPost(function(post) {
      res.send(post);
    });
  } else {
    database.getBlogPostByTitle(blogTitle, function(blogPost) {
      res.send(blogPost);
    });
  }
});

passport.use(new LocalStrategy(function(username, password, done) {
  // database.login(username, password, done);
  if (username === 'admin' && password === 'admin') {
    console.log('in');
    done(null, { username: username });
  } else {
    done(null, false);
  }
}));

app.post('/login', passport.authenticate('local', {
  successRedirect: '/accessed',
  failureRedirect: '/access'
}));





app.listen(8080);
console.log('Blog is running on port 8080');

}();

Thanks.

Jerry Chong
  • 7,954
  • 4
  • 45
  • 40
user2244925
  • 2,314
  • 3
  • 14
  • 11

11 Answers11

431

It looks like you didn't implement passport.serializeUser and passport.deserializeUser. Try adding this:

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

passport.deserializeUser(function(user, done) {
  done(null, user);
});
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • 2
    Worked for me. Why do I not have to use the 'id' part as written everywhere else? – schlenger Nov 13 '14 at 16:32
  • 11
    @schlenger it would depend on how you implement serialization. Sometimes you serialize by user id, which means that the `serializeUser` function stores just the user id in the session, and `deserializeUser` uses that id to retrieve the user data from a database (for instance). This is to prevent the session storage from containing the actual user data itself. – robertklep Nov 13 '14 at 17:22
  • +1 @robertklep comment. I'd always avoid serializing user information but only id's (for bloat/perf reasons personally). – electblake Jan 22 '16 at 20:03
  • 2
    @robertklep I don't want to store it in session. I am using JWT. Is serializeUser/deserializeUser required? I want a no session. I am using JWT – Internial Oct 30 '17 at 02:54
  • @Internial not sure if you need them, but it would be an easy thing to test. – robertklep Oct 30 '17 at 07:00
  • @robertklep I need them. could you please help by providing by an example or tutorial/book reference? Here is my current code. http://textuploader.com/dlkxz – Internial Oct 31 '17 at 23:57
  • Where do you implement serializeUser and deserializUser in the application? In a route? – Jake Wilson Feb 22 '19 at 07:21
  • @JakeWilson outside of any routes/middleware, so at the same level where you'd use `app.use(...)`, etc. – robertklep Feb 22 '19 at 07:38
  • @Internial why exactly you think you need them? Having session running on API server is not making sense, at least. – jayarjo Aug 14 '19 at 15:18
  • In my case, I was setting passport.serializeUser instead of calling it as a function. – Mike Apr 09 '20 at 02:38
61

If you decide not to use sessions, you could set the session to false

app.post('/login', passport.authenticate('local', {
  successRedirect: '/accessed',
  failureRedirect: '/access',
  session: false
}));
Kevin
  • 3,441
  • 6
  • 34
  • 40
  • 1
    But that won't turn off session on all other endpoints (where I also do not want any session) – jayarjo Aug 14 '19 at 15:20
20

Sounds like you missed a part of the passportjs setup, specifically these two methods:

passport.serializeUser(function(user, done) {
    done(null, user._id);
    // if you use Model.id as your idAttribute maybe you'd want
    // done(null, user.id);
});

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

I added the bit about ._id vs. .id but this snippet is from the Configure Section of docs, give that another read and good luck :)

electblake
  • 2,027
  • 18
  • 25
4

Here an working but still lazy way to use sessions and still "serialisize" the values.

var user_cache = {};

passport.serializeUser(function(user, next) {
  let id = user._id;
  user_cache[id] = user;
  next(null, id);
});

passport.deserializeUser(function(id, next) {
  next(null, user_cache[id]);
});

in case or weird errors just ask yourself: "Do I rlly set '_id' in my user object?" - in most cases you dont. So use a proper attribute as key.

2

Make sure you have used async and await when getting user data.

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

passport.deserializeUser(async (id, done) => {
  const USER = await User.findById(id);
  done(null, USER);
});

passport.use(
  new GoogleStrategy(
    {
      // options for google strategy
      clientID: keys.google.clientID,
      clientSecret: keys.google.clientSecret,
      callbackURL: "/auth/google/redirect",
    },
    async (accessToken, refreshToken, profile, done) => {
      //   passport callback function

      //   check if user already exist in our db
      const oldUser = await User.findOne({ googleId: profile.id });
      if (oldUser) {
        return done(null, oldUser);
      } else {
        const newUser = await new User({
          username: profile.displayName,
          googleId: profile.id,
        }).save();
        return done(null, newUser);
      }
    }
  )
);
1

use this code into your server.js file where u can make a get or post request.

  app.get('/auth/facebook/callback',passport.authenticate('facebook',{
    successRedirect:'/profile',
    failureRdirect:'/',
    session: false
}))
0

Using Promise with serializeUser & deserializeUser:

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

passport.deserializeUser((id, done) => {
  // console.log(`id: ${id}`);
  User.findById(id)
    .then((user) => {
      done(null, user);
    })
    .catch((error) => {
      console.log(`Error: ${error}`);
    });
});

Please see my github repo for a full code example how to solve this issue.

Isak La Fleur
  • 4,428
  • 7
  • 34
  • 50
0

You missed this code:

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

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

For details, check the session part in http://www.passportjs.org/docs/configure/

B. Go
  • 1,436
  • 4
  • 15
  • 22
Awsaf
  • 21
  • 5
0

Another prevalent reason for this as I discovered today is that if you are using the standard packages such as passport,passport-local,passport-local-mongoose and express-session and u are already in a session that is you were logging in and then you terminate the server and restart again, you will be in the same session while logging in where the browser keeps loading with no response and you get this error of "failed to serialize user into the session".

0

Since "user" is returned as an array of objects, try this:

passport.serializeUser(function(user, cb) {
    process.nextTick(function() {
        return cb(null, user[0].id);
    });
});
Islam
  • 1
  • 1
-3

in passport.use('local-login'...)/ or /('local-singup'...)

if err you have to return "false" err {return done(null, req.flash('megsign', 'Username already exists #!#'));} true {return done(null, false, req.flash('megsign', 'Username already exists #!#'));}