Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Express 4? #222

Closed
jgallen23 opened this issue Mar 31, 2014 · 66 comments
Closed

Support for Express 4? #222

jgallen23 opened this issue Mar 31, 2014 · 66 comments

Comments

@jgallen23
Copy link

Has anybody had any success using passport with Express 4?

@tbeseda
Copy link

tbeseda commented Mar 31, 2014

I upgraded my application today today and ran into problems with Passport.
I haven't had much time, but I suspect the trouble is related to the exclusion of stock middleware in Express 4.0.
Based on how easy it was to upgrade my other application, I'm guessing Passport will be similar.
If I find time, I'll look into what it takes to update Passport.

@tbeseda
Copy link

tbeseda commented Apr 2, 2014

Ah, it seems Passport works fine with Express 4. I was having trouble with my session library (connect-mongo) which made some assumptions about the express object it was passed.

So yes, I am successfully using Passport with Express 4. Just had to update my session module.

@jgallen23
Copy link
Author

great! I'll close this. Thanks @tbeseda

@Cengkaruk
Copy link

I have problem, i think the serializeUser not called after LocalStrategy.

router.post('/auth', passport.authenticate('local'), function(req, res) {
  res.send(req.user);
});

Response always Unauthorized, and the next function not called. I try to print something on the serializeUser and not show anything.

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

Here is my session log.

{ "_id" : "wgy3sLw8V1g5doxOzSI4i7OE", "session" : "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"passport\":{}}", "expires" : ISODate("2014-05-03T17:29:42.726Z") }

Yap, the passport object is empty.

@jihokoo
Copy link

jihokoo commented Apr 28, 2014

any progress on this? i am having the same problem with passport-twitter. passport object is empty

@tleen
Copy link

tleen commented Jun 8, 2014

I'm finding passport and Express 4 to be devilish together.Even if my authorization are going through req.user is not being set at all. Something is weird.

@mathiasm74
Copy link

Agree. I'm using connect-mongostore 0.1.0 for session middleware, and logging in with Passport works fine, but logging out does not clear the passport object from the session store, leaving the user still logged in. In my opinion this issue should not have been closed without more proof the module isn't broken.

@tleen
Copy link

tleen commented Jun 8, 2014

I'm not sure its passport, I think there is a problem with express-session and cookies, passport is just a common user of those.

@sshrshnv
Copy link

I use:
express 4.4.1, express-session 1.3.1,
passport 0.2.0, passport-local

And have the same problem.

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

String 1111111111111111111111111111111111100 is never logged.

@tleen
Copy link

tleen commented Jun 15, 2014

Passport gets the blame because it is relatively complex and has dependencies on other middleware. Passport is actually working good but I have found other middleware in the mix is messing up cookies/sessions and therefore affecting Passport as well. I would suggest cutting out other middleware till you find your problem. Some of the other middleware is not ready for Express 4 and its problems are showing through things like Passport.

@sshrshnv
Copy link

Wow, all works for me now!!

I had:

.post('/auth/', function (req, res, next) {
            passport.authenticate('local', function (err, user) {
                if (err) {
                    /* something */
                }
                res.json({ state: req.isAuthenticated() });
            })(req, res, next);
        })

it didn't work. State was false.
Than i try:

.post('/auth/', passport.authenticate('local', {
            successRedirect: '/',
            failureRedirect: '/login',
            failureFlash : true
        }))

And it works!
It was necessary to add req.logIn() to the first version. So

.post('/auth/', function (req, res, next) {
            passport.authenticate('local', function (err, user) {
                if (err) {
                    /* something */
                }
                if (user) {
                    req.logIn(user, function (err) {
                        /* something */
                    });
                }
                res.json({ state: req.isAuthenticated() });
            })(req, res, next);
        })

return true.
User is authenticated.
But it's false if server is restarted.

@Hoverbear
Copy link

I can confirm that express-session with a secret field will generate a new session every request. Switching off the secret fixes this, but is a security issue. Switching to cookie-session is an alternative.

@dstroot
Copy link

dstroot commented Jun 20, 2014

Holllleeee CRAP! That is exactly the issue! I am using Express 4 (latest) and Express-session (latest) and that is exactly what is happening!!!! I removed the secret and it works... obviously a bug.

I am using connect-mongo to store the sessions in a mongo db.

So is the issue with express-session or connect-mongo? I looked around and it doesn't seem to be noted in the express-session issue log.

@dougwilson
Copy link

I can confirm that express-session with a secret field will generate a new session every request. Switching off the secret fixes this, but is a security issue. Switching to cookie-session is an alternative.

FYI express-session does not function without a secret; if not providing a secret to express-session, it means you are probably providing a different secret to cookieParser() or similar; check what your req.secret is set to to see what your secret should be to express-session, maybe?

@dougwilson
Copy link

Hi everyone, please try version 1.5.0 of express-session and let me know if it fixes the issue.

@elendirx
Copy link

I'm using express-session 1.5.0 and the cookie is set and stable between requests. My problem is that the passport object inside the session is empty, even though serializeUser is successfuly called. The session looks like this (I'm using connect-mongo):

{
   "_id": "vGJkgpLo_ZZIL6tPP0y3VMNI-wQLkaJK",
   "session": "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"\/\"},\"passport\":{}}",
   "expires": ISODate("2014-07-04T08:24:24.465Z") 
}

EDIT: Found the solution, I had my own bug in deserializeUser method. So my issue was not connected to any of the modules. Confirming Express 4 + express-session 1.5.0 + connect-mongo working just fine.

@dougwilson
Copy link

Confirming Express 4 + express-session 1.5.0 + connect-mongo working just fine.

Awesome. Anyone else care to try it out as well :)?

@dstroot
Copy link

dstroot commented Jun 20, 2014

Doug:

I will this evening - thanks for the quick turnaround. Question:

I am currently using a different secret with cookie-parser and express-session. Should they be the same? Should I not be using signed cookies if I have a session secret? Basically want to confirm this is OK:

app.use(cookieParser('dog'));
app.use(session({secret: 'cat'}))

@dougwilson
Copy link

@dstroot they had to be the same before, but as of 1.5.0 it should no longer relay on that strange behavior and the secret you give to session will actually be the secret it will use when reading the cookies from the request.

@dougwilson
Copy link

TL;DR express-session prior to 1.5.0 basically used the secret you gave to cookie-parser when reading cookies, and it's own secret when writing cookies, which if you gave them different secrets, it could never read the cookies back. When you removed the secret argument to express-session, it actually internally used the secret to cookie-parser for both reading and writing, which is why it suddenly started working when you did that.

express-session 1.5.0+ no longer even requires you to use cookie-parser, as it will parse and set the cookies itself, thus eliminating this confusing inter-dependence.

@dstroot
Copy link

dstroot commented Jun 21, 2014

This is awesome - 1.5 works great for me without needing cookie-parser anymore! Whoohoo one less dependency. ;) Doug - you rock!

@crsrusl
Copy link

crsrusl commented Jun 25, 2014

I had this problem. The fix was to add the express-session secret into the cookie parser. problem solved.

So the code should be something like the below:

app.use(cookieParser('foo'));
app.use(expressSession({
secret : 'foo',
cookie : {
expires: false,
domain: config.cookie.domain
},
store: redisSessionStore
}));

notice the cookie parser contains the session secret.

@dstroot
Copy link

dstroot commented Jun 26, 2014

If you are using express-session >v1.5 you no longer need cookie parser at all

@zhex
Copy link

zhex commented Jul 3, 2014

i get the same problem when i using express-session 1.6.1 and connect-mongo 0.4.1. it create new session in store for every request. it works fine with cookie-session. but cookie-session not support store.

@lerouxb
Copy link

lerouxb commented Aug 4, 2014

Someone should probably update http://passportjs.org/guide/configure/ ...

@larissaleite
Copy link

@elendirx I think I might have a bug in deserializeUser. Could you show me yours, please?

@johwer
Copy link

johwer commented Oct 6, 2014

Thx dougwilson now it works without cookieParser. Don't forget to type in username and password in the browser even if you haven't setup a password check.

@andrewgleave
Copy link

Just to add my 2¢. The same apparent issue is also triggered by specific browser security settings.

For example, if you or your users have selected the 'Allow from current website only' option for cookies in Safari (OS X or iOS 8) new sessions will be created for each of the requests required to complete the authentication. This is not an issue when using the LocalStrategy, but will cause anon -> auth -> anon when using an OAuth2 provider. This echo what people above have said they've seen (initial authenticated session before a redirect).

@afreeland
Copy link

Anyone find a solution yet? I appear to be having the same problem that deserialization never gets called...req.user is not defined and regardless of what I do with sessions it comes up empty. Although I can see what appears to be previous session store data, just not current...because its awesome like that.

I am probably taking a different approach than most as this is really part of a .net mvc app and I really do not even need session support.

It is fairly frustrating to hand passport a user object and not be able to get it back...its like...I gave you this object, I just want it back

@afreeland
Copy link

Doubt this will help anyone that has the requirement of sessions, but I did manage to get some code working to at least persist and send my user in a response.

This code is what I am using for my FacebookStrategy callback

app.get('/auth/facebook/code', function(req, res, next){
        debugger;
            passport.authenticate('facebook', function (err, user, info) {
                if(err) return next(err);
                if(!user) return res.redirect('/fail');

                req.login(user, function (err) {
                    if(err) return next(err);
                    return res.send(user);
                });

            })(req, res, next);
    });

By handling the req, res manually and then calling passport.authenticate inside of my middleware...I am able to handle the request outside of the library. I wanted to post this here in case anyone had a similar issue with express 4 using passport as a 'potential' workaround.

@asonnenschein
Copy link

I'm still having this problem with express v4.10.6 and express-session v1.9.3...

Update: False alarm, I was writing tests and realized I wasn't persisting the session... I resolved the problem with the agent method in supertest:

var supertest = require('supertest);
var agent = supertest.agent(server);

@zachcowell
Copy link

I initially had trouble with Express 4 working with Passport, and it was due to a bonehead dumb mistake. Want to post here in case anyone else experiences a similar issue. Your session setup must be before passport.initialize() and passport.session() e.g.:

app.use(session({ 
    secret: 'brucespringsteinmegaboss',
    resave: false,
    saveUninitialized: true
}));

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

Lost a few hours to this one, hopefully this helps someone else ;)

@framerate
Copy link

@zachcowell thanks for that.

I wasn't using passport.session() in express 3 and I'm not sure why, but that line was the missing piece.

@nemanjavuk
Copy link

I'm not sure if anybody is still having issues with this but I'm having problems. req.session.passport and req.user are always empty and neither serializeUser nor deseiralizeUser aren't being called. I'm using the latest express 4.11.2, express-session 1.10.2, body-parser 1.11.0, passport 0.2.1 and passport-local 1.0.0 with mysql as a backend.
I'm setting up the express and passport like this:

var bodyParser = require('body-parser');
var session = require('express-session');
var MemoryStore = session.MemoryStore;
var _ = require('underscore');
var passport = require('passport');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
    key: 'KEY',
    secret: 'SECRET331156%^!fafsdaasd',
    store: new MemoryStore({reapInterval: 60000 * 10}),
    saveUninitialized: true,
    resave: false
}));
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport); // pass passport for configuration

Authentication strategies:

module.exports = function (passport) {
    passport.serializeUser(function (user, done) {
        logger.info('SERIALIZE USER');
        done(null, user.id);
    });

    passport.deserializeUser(function (id, done) {
        logger.info('DESEIRALIZE USER!');
        mysqllib.getConnection(function (err, connection) {
            if (err) {
                done(err);
            }
            var sql = "SELECT * FROM users WHERE idusers = ?";
            logger.info('sql: ' + sql);
            connection.query(sql, [id], function (err, rows) {
                connection.release();
                var user = {};
                user.id = rows[0].idusers;
                done(err, user.id);
            });
        });
    });

    passport.use('local-signup', new LocalStrategy({
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true // allows us to pass back the entire request to the callback
        },
        function (req, email, password, done) {
            logger.info('CALLING local-signup');
            var firstname = req.body.firstname;
            var lastname = req.body.lastname;
            var role = req.body.role;
            mysqllib.getConnection(function (err, connection) {
                var sql = "INSERT INTO users VALUES(0, ?, ?, ?, ?, null, ?, 0, null, null, null, null, null, null, 0, 0)";
                logger.info('sql: ' + sql);
                connection.query(sql, [email, password, firstname, lastname, role], function (err, rows) {
                    connection.release();
                    if (err) {
                        if (err.code == 'ER_DUP_ENTRY') {
                            logger.info('er_dup_entry');
                            return done(err);
                        } else {
                            logger.info('general err');
                            return done(err);
                        }
                    } else {
                        logger.info('everything is OK!');
                        var user = {};
                        user.id = rows.insertId;
                        req.session.user_auth = user.id;
                        return done(null, user);
                    }
                });
            });
        }));

    passport.use(
        'local-login',
        new LocalStrategy({
                usernameField: 'email',
                passwordField: 'password',
                passReqToCallback: true // allows us to pass back the entire request to the callback
            },
            function (req, email, password, done) { 
                mysqllib.getConnection(function (err, connection) {
                    if (err) {
                        logger.info('getConnection: ' + err);
                        return done(err);
                    }
                    var sql = "SELECT idusers, first_name, last_name, email, phone, dob, address, role, photo1, photo2, photo3, photo4, phonevalidated, uservalidated FROM users WHERE email = " + connection.escape(email) + " AND password = " + connection.escape(password);
                    connection.query(sql, function (err, rows) {
                        connection.release();
                        if (err) {
                            logger.error("select user", err);
                            return done(err);
                        } else if (rows.length) {
                            var user = rows[0];
                            user.id = rows[0].idusers;
                            return done(null, user);
                        } else {
                            logger.warn('Incorrect Login credentials, username: ' + email + ' password: ' + password);
                            return done(null, false, {message: 'unauthorized'});
                        }
                    });
                });
            })
    );
};

How I use a strategy in an express route:

app.post('/login', function (req, res, next) {
    passport.authenticate('local-login', function (err, user, info) {
        if (err) {
            mysend(res, 500, 'Ups. Something broke!');
        } else if (info) {
            mysend(res, 401, 'unauthorized');
        } else {
            mysend(res, 200, JSON.stringify(user));
            logger.info(req.user);
            logger.info(req.session);
        }
    })(req, res, next);
});

Everything works fine except the serializeUser and deserializeUser that aren't being called. I can even manually set a value in a session inside a strategy but I don't want to do that. I want passport to handle sessions.

@framerate
Copy link

@nemanjavuk it's likely you either are missing middleware or have it in the wrong order..

Here's mine:

app.use(serveStatic(__dirname + '/static', {'index':false}));
app.use(session(sessionConfig));
app.use(passport.initialize());
app.use(passport.session());
app.use(csrf());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(debugOutput);

Compare with yours?

@nemanjavuk
Copy link

@framerate As far as I can see the only difference is that you use bodyParser after the express and passport sessions but I do it before. I tried with your approach but it doesn't work either way.

@darth-cheney
Copy link

@nemanjavuk Did you ever find a solution? I'm having precisely the same problem. Neither serializeUser nor deserializeUser are even being called during the authentication process. All middleware seems to be loaded in the correct order too.

@nemanjavuk
Copy link

@darth-cheney As a matter of fact I have. I was missing to call req.login() in my custom callback which in fact calls serializeUser:

app.post('/login', function (req, res, next) {
    passport.authenticate('local-login', function (err, user, info) {
        if (err) {
            mysend(res, 500, 'Ups. Something broke!');
        } else if (info) {
            mysend(res, 401, 'unauthorized');
        } else {
            req.login(user, function(err) {
                if (err) {
                    mysend(res, 500, 'Ups.');
                } else {
                    mysend(res, 200, JSON.stringify(user));
                }
            }
        }
    })(req, res, next);
});

Hope it helps

@beaugunderson
Copy link

Noting that I too had many of these problems; my issue was that I was using MemoryStore. I switched to connect-redis RedisStore /w redis-url and they all went away. :)

In many of the examples above it seems like people are using MemoryStore as well (no store specified); maybe during development? Probably good to note that you can't do that and expect things to work correctly.

@BarthesSimpson
Copy link

I had the same issue with my serializeUser and deserializeUser functions never being called. My educated guess is that passport.authenticate('local') was causing the glitch in my middleware, because everything worked after I replaced that with a custom function as follows:

Instead of:

app.get(/^(.+)$/, passport.authenticate('local'), function(req, res){
    res.sendFile(__dirname + '/public/'+req.params[0]);
});

I used:

app.get(/^(.+)$/, ensureAuthenticated, function(req, res){
    res.sendFile(__dirname + '/public/'+req.params[0]);
});

function ensureAuthenticated(req, res, next) {  
  if (req.isAuthenticated()) { 
console.log('user logged in '+ req.user.username);
    next(); 
  } 
  else {   
    console.log('user not logged in ');
    res.redirect('/login');          
  };

};

@ORESoftware
Copy link

@Shershnev-AV thank you SOOOOO much for your answer, that is correct. I had the common problem that my serializeUser and deserializeUser callbacks were not being fired. It was because I was using the less common scenario that Shershnev-AV described (passport.authenticate('local', function (err, user, info){}) . Jared Hannson, take note. You are not documenting your software well enough.

@ORESoftware
Copy link

@larissaleite and @nemanjavuk ------> @Shershnev-AV 's answer above might solve your problem as I had the same problem as you

@jaredhanson
Copy link
Owner

Jared Hannson, take note. You are not documenting your software well enough.

@ORESoftware Hopefully you find it good enough for the price I charge for the software.

You are welcome to contribute the effort by writing new or expanding existing documentation.

@mercmobily
Copy link

@ORESoftware @jaredhanson Jared, thank you for your fantastic efforts with Passport and thank you for continuously going through the issue list and never abandon this module.

@BarthesSimpson
Copy link

Here, here. Passport is epic.

On Jun 20, 2015, at 4:49 AM, Tony Mobily notifications@github.com wrote:

@ORESoftware @jaredhanson Jared, thank you for your fantastic efforts with Passport and thank you for continuously going through the issue list and never abandon this module.


Reply to this email directly or view it on GitHub.

@simondelorean
Copy link

I generated my project with the Yeoman fullstack generator and didn't know how to apply @Shershnev-AV 's fix. However, @Awk34 has a nice solution here: https://github.com/Awk34/aksite.
He extended auth.service with a function called "appendUser" that acts as a middleware and adds the current user to the request.

@khrykin
Copy link

khrykin commented Dec 24, 2015

In my case, deserializeUser is called ONLY in Safari and not in Chrome, Firefox etc... So, in Safari req.user exists after page refresh, but in other browsers not. Just can't figure out why... I'm using postgres as session storage and sequelize-passport-local for retrieving user record. Here is my setup:

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

app.use(session({
  store: new SequelizeStore({
    db: sequelize
  }),
  secret: SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 30 * 24 * 60 * 60 * 1000,
   }
}));

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

passport.use(User.createStrategy()); // sequelize-passport-local
passport.serializeUser(User.serializeUser()); // sequelize-passport-local
passport.deserializeUser(User.deserializeUser()); // sequelize-passport-local

passport.deserializeUser(function(id, done) {
  console.log("DESERIALIZE");
  console.log(id);
});

app.post('/login', (req, res, next) => {
  passport.authenticate('local', (err, user, info) => {
    if (err) {
      return next(err);
    }
    if (! user) {
      return res.json({ error: info.message }); // Don't want 401 here
    }
    req.logIn(user, function(err){
      if(err){
        return next(err);
      }
      let {id, name, email, picture } = user;
      res.json({id, name, email, picture });
      return next();
    });

  })(req, res, next);

});

"DESERIALIZE" never prints in Chrome, Firefox, but always does in Safari (as expected).
Any help please, the above solutions seem to not work for me, just have killed the whole day with this issue.

@choonkending
Copy link

@khrykin Did you ever find out what the cause of your issue was? I am encountering the same issue (working on safari but not chrome), and have tried the above solutions as well, to no avail :(

I do not think passport is causing this though, but just wondering whether you found a fix for yours. My hunch is that express-session is causing this. :(

@khrykin
Copy link

khrykin commented Jan 8, 2016

@choonkending Yes, I did, my apologies for not posting it here. I used fetch to make login request and without setting { credentials: 'same-origin' } option, cookies were not sent to the server (expected behavior as by fetch's documentation) BUT excluding safari, which sends cookies anyway (unfortunately for me because I actually set credentials option, but did it with typo, and had hard times trying to debug this... NUFF SAID).
So anyway, the problem is likely hides in the way you send requests from the client.

@choonkending
Copy link

@khrykin Legend! This kept me up several nights. Thanks mate.

@sumitkm
Copy link

sumitkm commented Aug 7, 2016

@nemanjavuk You are a legend! I have been going round in circles for almost a week now! Thanks for the tip about calling req.login, though I am not sure why I had to do it and why passport-local-mongoose wasn't doing it by itself. I'll continue with that research (its probably a pbkac error ;-)...), but for now, my sessions have the user associated woo hoo!!! Thank you!

@BenTsai85
Copy link

I had the same problem as mentioned, deserializeUser is never called. But I found that findById is a Promise and will never be resolved...

User.findById(id, function(err, user) {
            console.log(user);
            done(err, user);
        });

So if you have the same problem as me using findById, use the following code instead:

    passport.deserializeUser( async(id, done) => {
        console.log("deserialize");
        User.findById(id)
            .then( user => {
                console.log(user);
                done(null, user);
            })
            .catch( err => {
                console.error(err);
                done(err);
            });
    });

@Aaronius
Copy link

@khrykin I just spent a good 5 hours trying to figure this out. Thank you!

@rohanparmar27
Copy link

passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
if (_.isEmpty(user))
throw new Error(user not found)
done(null, user.toJSON());
}).catch(e => {
done(e)
})
});

@BenTsai85 u can set it and try

@thomasgauvin
Copy link

@zachcowell thank you for sharing this, I was getting issues with Passport returning Bad Request with no helpful errors logs, your post shed light into the cookie: { secure: true } setting that I had, which was causing the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests