13
votes

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);
    });

};
2
I'm having the same issue, have you solved this?Jeffpowrs
Nope, will answer here when i have solution. Gave it a timeout. By far i did all i can with that, it doesn't work )Rantiev
I assume login request is setting the cookie which is then passed back in subsequent request and that cookie has the access token which you issued.Nitin Midha
That's correct. But express session or passport session is never restored, and that's why it doesn't call deserialize method. I don't know why. I see that cookie set properly and it's being passed to backend in auth check request.Rantiev
@Rantiev, you may want to make life easier for yourself and use the cookie-session middleware, instead of the express-session middleware, it will be less setup and less time and less headache.Daniel

2 Answers

2
votes

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.

1
votes

Have you tried maxAge?

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