Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

req.session.passport is empty, deserializeUser not called - ExpressJS, Passport

I am having problems using sessions in Passport/ExpressJS.

When I log req.session, I can see that passport is {}:

{ cookie: 
 { path: '/',
  _expires: Mon Sep 29 2014 19:37:16 GMT-0300 (BRT),
  originalMaxAge: 3594522,
  httpOnly: true,
  secure: true },
passport: {} }

Also, I am authenticating with Facebook, and passport.deserializeUser is not being called. This is my code:

    passport.use(new FacebookStrategy({

    // pull in our app id and secret from our auth.js file
    clientID        : configAuth.facebookAuth.clientID,
    clientSecret    : configAuth.facebookAuth.clientSecret,
    callbackURL     : configAuth.facebookAuth.callbackURL

},

// facebook will send back the token and profile
function(token, refreshToken, profile, done) {

    // asynchronous
    process.nextTick(function() {

        console.log("profile " + profile.id);
        console.log("profile.name "+profile.name.givenName)

        // find the user in the database based on their facebook id
        User.findOne({ 'facebook.id' : profile.id }, function(err, user) {

            // if there is an error, stop everything and return that
            // ie an error connecting to the database
            if (err)
                return done(err);

            // if the user is found, then log them in
            if (user) {
                //returning undefined
                //console.log(user.name + " " + user.email);
                return done(null, user); // user found, return that user
            } else {
                // if there is no user found with that facebook id, create them
                var newUser = new User();

                // set all of the facebook information in our user model
                newUser.facebook.id    = profile.id; // set the users facebook id
                newUser.facebook.token = token; // we will save the token that facebook provides to the user
                newUser.facebook.name  = profile.name.givenName + ' ' + profile.name.familyName; // look at the passport user profile to see how names are returned
                newUser.facebook.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first

                // save our user to the database
                newUser.save(function(err) {
                    if (err)
                        throw err;

                    // if successful, return the new user
                    return done(null, newUser);
                });
            }

        });
    });

}));

// used to serialize the user for the session
passport.serializeUser(function(user, done) {
    console.log("serialize");
    done(null, user.id);
});

// used to deserialize the user
passport.deserializeUser(function(id, done) {
    console.log("deserialize");
    User.findById(id, function(err, user) {
        console.log(user);
        done(err, user);
    });
});

I've tried moving both methods to the beginning but it didn't make any difference. passport.serializeUser is called just fine.

This is where I initiliaze passport:

app.use (cookieParser());
app.use(session({ secret: 'appsecret', saveUninitialized: true, cookie: { secure: true, maxAge: new Date(Date.now() + 3600000) }, key:'connect.sid' }));

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

And this is my route:

// =====================================
// FACEBOOK ROUTES =====================
// =====================================
// route for facebook authentication and login

app.get('/auth/facebook', passport.authenticate('facebook', { scope : 'email' }));

// handle the callback after facebook has authenticated the user
app.get('/auth/facebook/callback',
    passport.authenticate('facebook', {
        successRedirect : '/home',
        failureRedirect : '/login'
    }));

Can someone help me with this?

Thanks!

like image 443
Larissa Leite Avatar asked Sep 29 '14 21:09

Larissa Leite


People also ask

What is serializeUser and deserializeUser in passport?

Passport uses serializeUser function to persist user data (after successful authentication) into session. Function deserializeUser is used to retrieve user data from session.

What is passport ExpressJS?

Passport is Express-compatible authentication middleware for Node. js. Passport's sole purpose is to authenticate requests, which it does through an extensible set of plugins known as strategies.

What is Passportjs for?

Passport. js is a popular Express middleware specifically created to facilitate the login process. It is flexible, trusted by many organizations worldwide, and easy to integrate into your ExpressJS code. In this tutorial we'll: Create a login form for a Node application using Passport.

What is passport library?

The Passport library provides more than 500 authentication mechanisms, including OAuth, JWT, and simple username and password based authentication. Using Passport makes it easy to integrate more than one type of authentication into the application, too.


2 Answers

Background

For me the issue with deserializeUser not being called and empty passport object was cross-domain issue. When I was sending ajax request from localhost:4200 to localhost:1000 cookies and sessions didn't work for my Ember.js client. However, Postman extension received correct response. Also passport object was filled with data correctly. User.deserializeUser was getting called correctly.

Solution

To solve this I had to set correct server-side and client HTTP headers. Server-side(node.js + express + passport):

# The important part. Must go AFTER the express session is initialized
app.use passport.initialize()
app.use passport.session()

# Enable CORS
# X-Requested-With, X-AUTHENTICATION, X-IP
# https://stackoverflow.com/a/18290875/2166409
app.use (req, res, next) ->
  res.header 'Access-Control-Allow-Origin', 'http://localhost:4200'
  res.header 'Access-Control-Allow-Headers', 'Origin, X-Requested-With, X-AUTHENTICATION, X-IP, Content-Type, Accept'
  res.header 'Access-Control-Allow-Credentials', true
  res.header 'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'
  next()

app.use '/api/v1', router

Client-side:

If using jQuery add this to $.ajax options:

xhrFields: { withCredentials:true }

Then everything works as expected:

DESERIALIZE USER
{ id: '547fabf22a098ade53e6e48e',
  name: 'test',
  firstName: 'test',
  email: '[email protected]' }
LOGIN SUCCESS SESSION
{ cookie: 
   { path: '/',
     _expires: Thu Dec 04 2014 20:26:52 GMT+0100 (CET),
     originalMaxAge: 3597205,
     httpOnly: false },
  passport: 
   { user: 
      { id: '547fabf22a098ade53e6e48e',
        name: 'test',
        firstName: 'test',
        email: '[email protected]' } } }

These answers helped me: about Ember.js, jQuery.ajax.

like image 122
Daniel Kmak Avatar answered Sep 21 '22 17:09

Daniel Kmak


For me, the root cause was the cookie security value in the express-session.

app.use(require('express-session')({
    secret: 'crackalackin',
    resave: true,
    saveUninitialized: true,
    cookie : { secure : false, maxAge : (4 * 60 * 60 * 1000) }, // 4 hours
}));

On my local machine, I wasn't running over HTTPs so when cookie.secure is true, the session and user object were empty. When in dev mode and secure was false, it worked correctly.

like image 30
Greg Avatar answered Sep 18 '22 17:09

Greg