Rantiev
Rantiev

Reputation: 2242

passport deserializeUser method never called

I have got 200 response for login request, but 401 for any futher auth check requests, because deserializeUser never called. I dived into passport source and noticed that passport checks whether req._passport.session.user exists, and if no it doesn't call deserializeUser.

I have searched through other questions on stackoverflow, it seems i have specific case.

There is single local strategy auth type, i use Ajax request to make login requests, CORS settings configured, http://localhost:8080 - frontend, http://localhost:3000 backend)

I use bodyParse, cookieParser, express session, passport initialize and passport sessions. Express session secure:false configured as i run auth requests through http.

You can find my project here (backend package.json is good to go, so you can use it, it has no missing dependencies, as for frontend not sure), at least you can check the code there.

Backend https://github.com/rantiev/template-api Frontend https://github.com/rantiev/template-angular

Express session configuration and CORS is here https://github.com/rantiev/template-api/blob/master/modules/appConfigure.js

var path = require('path');
var bodyParser = require('body-parser');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var MongoStore = require('connect-mongo')(session);

module.exports = function (app, express, config, mongoose) {

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

    app.use(function (req, res, next) {

        // Website you wish to allow to connect
        res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');

        // Request methods you wish to allow
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

        // Request headers you wish to allow
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, X-AUTHENTICATION, X-IP, Content-Type, Origin, Accept, Cookie');

        // Set to true if you need the website to include cookies in the requests sent
        // to the API (e.g. in case you use sessions)
        res.setHeader('Access-Control-Allow-Credentials', true);

        // Pass to next layer of middleware
        next();
    });

    /*app.use(function (req, res, next) {
        console.log('coockie is:', req.cookies);
    });*/

    app.use(session({
        saveUninitialized: false,
        resave: false,
        secret: config.sessionsSecretToken,
        cookie: {
            secure: false
        },
        store: new MongoStore({ mongooseConnection: mongoose.connection })
    }));

    app.use(express.static(path.join(__dirname, '..' , 'public')));

};

Passport configuration is here https://github.com/rantiev/template-api/blob/master/api/authentication/authenticationR.js

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var rememberMe = require('../../modules/rememberMe');
var createAccessToken = require('../../modules/createAccessToken');

var bcrypt = require('bcrypt-nodejs');

var UserM = require('../users/userM');

module.exports = function (app, mainRouter, role) {

    passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password'
    }, function (username, password, done) {

        UserM.findOneQ({email: username})
            .then(function(user){

                if (user && bcrypt.compareSync(password, user.password)) {
                    done(null, user);
                } else {
                    done(null, false);
                }

            })
            .catch(function(err){
                done(err);
            });

    }));

    passport.serializeUser(function (user, done) {

        console.log('serialize');

        if (user) {
            createAccessToken(user, done);
        } else {
            done(null, false);
        }
    });

    passport.deserializeUser(function (token, done) {

        console.log('deserialize');

        UserM.findOneQ({accessToken: token})
            .then(function(user){

                if (user) {
                    done(null, user);
                } else {
                    done(null, false);
                }

            })
            .catch(function(err){
                done(err);
            });

    });

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

    mainRouter.post('/me', passport.authenticate('local'), function (req, res) {
        res.status(200).send();
    });

    mainRouter.get('/logout', function (req, res) {
        req.logout();
        res.redirect('/');
    });

    mainRouter.get('/me', function (req, res) {

        if (!req.user) {
            res.status(401).send('Please Login!');
            return;
        }

        var currentUser = {
            id: req.user._id,
            role: req.user.role
        };

        res.status(200).json(currentUser);
    });

};

Upvotes: 14

Views: 3162

Answers (2)

nikk wong
nikk wong

Reputation: 8670

If you look in your call stack and find that deserializeUser is not being called because req._passport.session.user is not set, then your problem is as follows. The offending lines are in the express-session module:

if (!req.sessionID) {
  debug('no SID sent, generating session');
  generate();
  next();
  return;
}

If sessionID is set, generate is never called:

store.generate = function(req){
 req.sessionID = generateId(req); 
 req.session = new Session(req); // THIS
 req.session.cookie = new Cookie(cookieOptions);

 if (cookieOptions.secure === 'auto') {
  req.session.cookie.secure = issecure(req, trustProxy);
 }
};

But it is possible to have req.sessionID set, while req.session is null, which explains req._passport.session.user being null—req.session is never set.

I continued to trace back even further to when req.sessionID is set, which, with new cookies, was sometimes being set and sometimes not.

Why? I don't know, and would love for someone to investigate further, but, basically, the lesson is try using the cookie-session module instead.

Upvotes: 3

malix
malix

Reputation: 3572

Have you tried maxAge?

app.use(express.session({   store: sessionStore,
                            cookie: { maxAge : 3600000 } //1 Hour
                            }));

Upvotes: 1

Related Questions