81
votes

My question is similar to this one, but there was no insight into his solution.

I'm using Passport to auth using Instagram. After successful auth, users are directed to "/". At this point, the request has the user object (aka it's working). However, once I redirect, the req.user is undefined. :'(

The odd part is that passport.deserializeUser is being called with each request. It's successfully getting the user object, but somewhere along the middleware road, req.user is not being set (or being unset).

// on successful auth, goto "/" 
app.get('/', function(req, res) {
    // if the request has the user object, go to the user page
    if (req.user) {
        res.redirect("/user/" + req.user._id);
    }

    res.render("index");
}

app.get('/user/:uid', function(req, res) {
    console.log(req.user) // undefined
}
26
Could you post the configuration of your app? Middleware (app.use(...)) especially. It could be that your session cookies get a too low expiry, or that you have the order of the middleware wrong.robertklep
middleware ordering is most likely the issue hereNoah
I do the middleware EXACTLY like they explain in the Passport configuration page, and still face the same issue o.Ovsync
Did you ever find a solution?Strawberry
I got the same issue. I can't get it to work and it seems that the req.user got reset everytimeGang Su

26 Answers

50
votes

Have you set up session state for your app? If you haven't, you need something like this...

app.use(session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());
64
votes

My issue was not specifying to send cookies when using fetch on the client-side. It worked after including the credentials: 'include' field in the request.

fetch('/api/foo', {credentials: 'include'})
18
votes

I am super new to Node but it was a middleware issue for me. Added bodyParser and rearranged to fix.

Broken code:

app.use(express.cookieParser('secret'));
app.use(express.cookieSession());
app.use(express.session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public'))); 

Working Code:

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'anything' }));
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);

Hope this helps

13
votes

Previously I have these code (did not work):

app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('abcdefg'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
    secret: 'abcdefg',
    resave: true,
    saveUninitialized: false,
    cookie: { secure: true } // this line
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(require('stylus').middleware(path.join(__dirname, 'public')));

Then I remove the cookie option from session initializer:

app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('abcdefg'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
    secret: 'abcdefg',
    resave: true,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(require('stylus').middleware(path.join(__dirname, 'public')));

and now it works.

7
votes

I encounter the same problem because of just copy & paste following code from express-session documentation, but not fully read the documentation.

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

In the documentation, it mentioned:

However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies. If secure is set, and you access your site over HTTP, the cookie will not be set.

so if you just have a HTTP connection, remove the secure option,and below code should work:

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  //cookie: { secure: true } remove this line for HTTP connection
}))
4
votes

I had this exact same problem using express 4 and after trying pretty much everything I could find online I finally managed to get it working by adding 'cookie-session'.

var cookieSession = require('cookie-session');

app.use(cookieSession({
  keys: ['key1', 'key2']
}));
4
votes

Yeah enable sessions like @Martin says. But if you are using Express 4.* version, middleware like sessions are not bundled so you need to install it individually.

  1. Add "express-session": "1.4.0" in your package.json
  2. npm install
  3. use it

;

var express = require('express');
var cookieParser = require('cookie-parser')
var session = require('express-session')
var app = express()
app.use(cookieParser()) // required before session.
app.use(session({secret: 'keyboard cat'}))

For more information check the express session documentation.

4
votes

I think everyone has several mistakes on server-side which can cause this problem.

To solve this error, please check these things:

Check 1 - Passport Serialization and Deserialization

The correct way to do it (Mongoose):

passport.serializeUser((user, done) => {
    done(null, user._id);
});

passport.deserializeUser((_id, done) => {
  User.findById( _id, (err, user) => {
    if(err){
        done(null, false, {error:err});
    } else {
        done(null, user);
    }
  });
});

Check 2 - Sessions

Check your cookie-session package is configured properly and working. Check if you can actually see cookie on client-side. If you are using express-session please make sure you can see session-id in memory or cache(Redis).

Check 3 - SSL nginx

Make sure your reverse proxy supports cookies and request type. Don't forget to check cookie/session configuration [secure flag]

When I face this error, I was using (user) for callback function. Then I corrected it with (err, user). Now its all right. Cheers 🍻

3
votes

In routes, try adding: {session: true}

app.get('/auto-login', passport.authenticate('cross', {session: true}))
3
votes

When you authenticate a user, the request value req.user is filled. To know if the identify the user and fill that value, a cookie is used. If like me, you configure the session with secure cookies, cookies will be available only over HTTPS, which is not the standard configuration for, let's say, a local development server. To have cookies work over HTTP, comment out cookie: { secure: true } and req.user value will be properly configured:

this.app.use(session({
    resave: true,
    saveUninitialized: true,
    secret: process.env.SESSION_SECRET || "a secret",
    // cookie: { secure: true },
}));

If, reasonably, you want cookies over HTTPS only in production, you can do something like:

cookie: { secure: process.env.ENV === 'PRODUCTION' }
1
votes

Mine was about cookies & cors. I solved by adding following code to my server:

allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // your website
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
if ('OPTIONS' === req.method) {
    res.send(200);
} else {
    next();
}};
1
votes

Once you have your express session setup make sure you have passport serialize & deserialize, in the deserialize function that's when req.user gets generated. See explanation here from another stackoverflow question.

passport.serializeUser(function(user, done) {
 done(null, user);
});


passport.deserializeUser(function(user, done) {
 done(null, user);
});
1
votes

When u use http request on client side, remember to attach credentials. In Angular2 you need to add option argument: { withCredentials: true }. After this you will have the same req.sessionID until you reset it again.

this._http.get("endpoint/something", { withCredentials: true })
1
votes

Mine was different from all these. I had been previously developing on localhost a Wordpress site and then switched to a separate Node/React project.

The cookie from the Wordpress site was still present and being sent since both projects ran on localhost. This was causing the issue.

I had to clear the cookies for localhost and then it worked.

1
votes

'req.user' was being set to the user for me only after passport.auth middleware. I came to this thread trying to find out how to access the username from other requests (without passport.auth middleware), if anyone else is stuck on that issue then try

req._passport.session.user;
0
votes

If your middleware stack is unclear, assuming you have session middleware somewhere, you can save the session before the redirect:

if (req.user) {
    req.session.save(function (err) {
        if (err) {
            // ... panic!
        }
        res.redirect("/user/" + req.user._id);
    });
    return;
}
0
votes

Have you tried to insert in the cookieParser your secretKey?

var passport = require('passport');
var expressSession = require('express-session');

app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser('mySecretKey'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(expressSession({
secret: 'mySecretKey',
  resave: false,
  saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
0
votes

Had exactly the same problem. For me, the solution was to use "client-sessions" instead of 'express-session". Probably bad sessions configuration?

Not working code:

var session = require('express-session');

app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(session({
    secret: 'random string',
    resave: true,
    saveUninitialized: true,
    cookie: { secure: true }
}));

Working code:

var session = require('client-sessions');

        app.use(bodyParser.urlencoded({ extended: false }));
        app.use(express.static(path.join(__dirname, 'public')));
        app.use(cookieParser());
        app.use(bodyParser.json());
        app.use(session({
        cookieName: 'session',
        secret: 'random string',
        duration: 30 * 60 * 1000,
        activeDuration: 5 * 60 * 1000,
    }));
0
votes

I had same issue, both with req.isAuthenticated() and req.user, here is how I resolved

  • req.isAuthenticated() resolved by replacing findOne() with find() in findById() method inside deserialize(), then I could save authenticated req, else it was returning nothing.

  • req.user resolved by adjusting order, first express-session should be stored then passport should be initialized then next session store in passport.session() and after that we can access req.user, after saving session in passport.session()

app.use(session(...));
app.use(passport.initialize());
app.use(passport.session());
// Now we can access req.user so after we will call req.user, if we write it above these, it will always return underfined
app.use(function(req, res, next){
  res.locals.user = req.user || null
  next();
})

See, if you are accessing req.user after passport.session(), if not add your route below.

0
votes

While doing passport.authenticate you can add session: true to resolve your issue:

app.post("/login", passport.authenticate('local', {
    'failureRedirect': '/login',
    'session': true
}), (req, res)=>{ res.redirect("/"); });
0
votes

My two cents: not working code:

server.use(
session({
  secret: "secretsssss",
  rolling: false,
  resave: false,
  saveUninitialized: false,
  cookie: {
    sameSite: true, //this line
    maxAge: 60 * 60 * 1000
  }
})
);

working code:

server.use(
session({
  secret: "secretsssss",
  rolling: false,
  resave: false,
  saveUninitialized: false,
  cookie: {
    sameSite: false, // i think this is default to false
    maxAge: 60 * 60 * 1000
  }
})
);
0
votes

Server sends SetCookie header then the browser handle to store it, and then the cookie is sent with requests made to the same server inside a Cookie HTTP header.

I had to set withCredentials: true in my client. (axios.js)

const config = {
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
  },
};

axios.put(url, { page: '123' }, config)
  .then(res => console.log('axios', res))
  .catch(err => console.log('axios', err));

Then CORS problems occur.

So I added this to my express server:

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Origin', req.headers.origin);
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
  if ('OPTIONS' == req.method) {
    res.send(200);
  } else {
      next();
  }
});
0
votes

Ran into this same problem, however mine was more related to a misunderstanding of the difference between passport.authorize and passport.authenticate in the start of the OAuth flow and OAuth callback routes.

passport.authorize won't affect the req.user variable, but since I just copy-pasta'd the sample code from the strategy's docs, I didn't realize the wrong method was being used. After changing to passport.authenticate the user variable was available.

Hope this helps anyone who may be unknowingly doing the same thing.

0
votes

Change the order from this:

app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: true }));

to this:

app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");
0
votes

I was using cookie-parser along with express-session like this:

var express = require("express"); // 4.16.1
var cors = require("cors"); // 2.8.5
var cookieParser = require("cookie-parser"); // 1.4.4
var expressSession = require("express-session"); // 1.17.1

var app = express();

app.use(
  cors({
    origin: "http://localhost:3000",
    credentials: true,
  })
);

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


app.use(
  expressSession({
    secret: "secret123",
    resave: true,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 60 * 24 * 1000 },
  })
);
app.use(cookieParser("secret123")); // this line

Putting cookie-parser before express-session worked for me, like this:

var express = require("express"); // 4.16.1
var cors = require("cors"); // 2.8.5
var cookieParser = require("cookie-parser"); // 1.4.4
var expressSession = require("express-session"); // 1.17.1

var app = express();

app.use(
  cors({
    origin: "http://localhost:3000",
    credentials: true,
  })
);

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

app.use(cookieParser("secret123"));
app.use(
  expressSession({
    secret: "secret123",
    resave: true,
    saveUninitialized: true,
    cookie: { maxAge: 60 * 60 * 24 * 1000 },
  })
);
-1
votes

I had a different issue, something with bcrpt-node and arrow functions.

Code that works:

userSchema.methods.generateHash = function (password) {
  return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); 
};

userSchema.methods.isValidPassword = function (password) {
  return bcrypt.compareSync(password, this.password);
};

Code that does not work:

userSchema.methods.generateHash = (password) => {
  return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

userSchema.methods.isValidPassword = (password) => {
  return bcrypt.compareSync(password, this.password);
};