Michael Xue
Michael Xue

Reputation: 21

nodejs: "req.session.save is not a function" error during authentication

I am using PassportJS to authenticate users in my application. After a user logs in, the session is created, but soon after being redirected, the session appears to become undefined once again because it hasn't been saved. I found online that often times with redirects, the redirect completes before the session is saved, and so it's as if authentication never happened. The apparent solution is to use the req.session.save function so that redirects will only happen after the session is saved. However, I am getting an error log of "TypeError: req.session.save is not a function." Can somebody please help?

Here is my code for app.js.

var express = require('express'),
      passport = require('passport'),
      session = require('express-session'),
      bodyParser = require('body-parser'),
      RedisStore = require('connect-redis')(session),
      redis = require('redis'),
      logger = require('morgan'),
      errorHandler = require('express-error-handler'),
      site = require('./site'),
      oauth2 = require('./oauth2'),
      port = process.env.PORT || 8080;

var app = express();

var redisClient = redis.createClient(8080, 'localhost');

// use sessions for tracking logins
app.use(session({
  secret: 'keyboard cat',
  resave: true,
  saveUninitialized: true,
  store: new RedisStore({
    client: redisClient,
    host: "pub-redis-14280.us-central1-1-1.gce.garantiadata.com",
    port: 12543,
    ttl: 260
   })
}));

app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json({ type: 'application/json' }));

app.use(passport.initialize());
app.use(passport.session());
app.use(errorHandler({ dumpExceptions: true, showStack: true }));

// use ejs as file extension for views
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/views'));

// use passport
require('./auth');

// Account linking
app.get('/', site.index);
app.get('/login', site.loginForm);
app.post('/login', site.login);
app.get('/logout', site.logout);
app.get('/authorize', oauth2.authorization);
app.post('/authorize/decision', oauth2.decision);

// set up local server
if (module === require.main) {
    // [START server]
    // Start the server
    var server = app.listen(process.env.PORT || 8080, function () {
        var port = server.address().port;
        console.log('App listening on port %s', port);
    });
    // [END server]
}

module.exports = app;

site.js:

var passport = require('passport');
var login = require('connect-ensure-login');

// get layout
exports.index = function (req, res) {
  console.log("layout loaded");
  res.render('layout');
}

// get login form
exports.loginForm = function (req, res) {
  console.log("login page loaded");
  res.render('login');
}

// post login form
exports.login = [
  passport.authenticate('local'),
  function (req, res) {
    req.session.save(function (err) {
      res.redirect('/');
    });
  }
]

// logout
exports.logout = function (req, res) {
  req.logout();
  res.redirect('/');
}

Passport serialize/deserialize user:

passport.serializeUser(function(id, done) {
  console.log("serializing user");
  done(null, id);
});

passport.deserializeUser(function(id, done) {
  console.log("deserializing user");
  done(null, id);
});

In my passport authentication, I return the user id for simplicity, since that's all I need to represent users in my system.

Upvotes: 2

Views: 3191

Answers (1)

emm
emm

Reputation: 21

In case anybody else is still having this issue (like me), try following Nathan's comment above and debug your connection to your redis/mongo/etc store.

What worked for me was I had my redis host set to http://localhost so I swapped it to 127.0.0.1 (local development of course) and everything immediately worked.

Upvotes: 2

Related Questions