Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

};
like image 454
Rantiev Avatar asked Mar 27 '16 15:03

Rantiev


People also ask

What is Passport authentication?

Passport is a popular, modular authentication middleware for Node. js applications. With it, authentication can be easily integrated into any Node- and Express-based app. The Passport library provides more than 500 authentication mechanisms, including OAuth, JWT, and simple username and password based authentication.

What is passport strategy?

Passport's local strategy is a Node. js module that allows you to implement a username/password authentication mechanism. You'll need to install it like any other module and configure it to use your User Mongoose model.

How does Passportjs work?

The “Passport JS” library connects with the “expression-session” library, and forms the basic scaffolding to attach the (authenticated) user information to the req. session object. The main Passport JS library deals with already authenticated users, and does not play any part in actually authenticating the users.


2 Answers

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.

like image 180
nikk wong Avatar answered Oct 11 '22 12:10

nikk wong


Have you tried maxAge?

app.use(express.session({   store: sessionStore,
                            cookie: { maxAge : 3600000 } //1 Hour
                            }));
like image 26
malix Avatar answered Oct 11 '22 12:10

malix