90

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
}
Community
  • 1
  • 1
gojohnnygo
  • 1,148
  • 1
  • 10
  • 13
  • 4
    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 May 08 '13 at 07:30
  • middleware ordering is most likely the issue here – Noah May 08 '13 at 14:37
  • 1
    I do the middleware EXACTLY like they explain in the Passport configuration page, and still face the same issue o.O – vsync Aug 07 '13 at 17:13
  • I also have this problem. Somehow, the req.user is not defined 90% of the time. – Eric Smekens Oct 09 '13 at 06:19
  • 1
    I got the same issue. I can't get it to work and it seems that the req.user got reset everytime – Gang Su May 28 '14 at 19:14
  • For me it was a CORS issue, https://stackoverflow.com/a/49001128/3994271 solved this. – Ayan Feb 27 '18 at 04:35
  • You can take reference from here, https://stackoverflow.com/a/56111357/4701635 – Paresh Barad May 13 '19 at 11:28
  • If req.user works in your application's main module but is Undefined in others, check https://stackoverflow.com/questions/26781263/node-js-express-passport-routing – golimar Sep 22 '20 at 11:14
  • after trying all solutions above i was still having the issue of req.user being undefined ; also my serializeUser and deserializeUser was not running . and trying to re-login would cause an error "req.session.regenerate is not a function" which led me [here](https://github.com/jaredhanson/passport/issues/907) ... and i downgraded my passport to version 0.5 and it is working now – mushfik r Aug 07 '23 at 16:11

30 Answers30

77

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'})
Jon Rodness
  • 771
  • 5
  • 2
  • 2
    To expand a bit, stuff like cookies are only sent by default if the request is coming from the same origin. Otherwise, you have to use the `withCredentials` flag to specify that you do in fact want cookies to be sent back. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials. And note that if you're using something like Axios instead of the fetch API, the syntax will be different. – Adam Zerner May 06 '19 at 18:40
55

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());
Martin
  • 5,392
  • 30
  • 39
  • 30
    I did exactly what you wrote and I am also getting the `undefined` message. – vsync Aug 07 '13 at 16:58
  • 4
    Think this is actually `app.use(session({secret : 'anything'});` now that express-session is its own package and not part of express – gards Jul 08 '14 at 15:07
  • 2
    I'm still having the issue after setting up expression. I'm calling passport function after initialize express-session and I can use sessions with no problems in the app. – Pietro Coelho Aug 27 '15 at 04:00
  • 1
    @martin Is `passport.session()` required if I don't use sessions? – Zlatko Mar 14 '16 at 21:22
  • 1
    @Zlatko I never tried this myself, but there's an answer [here](http://stackoverflow.com/questions/25514029/passport-js-authentification-without-sessions) you could try. – Martin Mar 15 '16 at 08:33
  • 1
    Thanks. Tried that and a million other things, but I have several strategies already and it's probably too messy. Need to clean up first I guess. – Zlatko Mar 15 '16 at 08:40
  • 3
    This solution worked for me in the sense that I had my app configurations in the wrong order. When I put them in the order above, the problem was fixed. – fraxture May 25 '16 at 07:40
  • Aren't you defining the session twice here?? – fraxture May 30 '16 at 09:59
19

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

leylandjacob
  • 201
  • 2
  • 5
  • 2
    This helped me. Why is the second ordering of the middleware work and not the first? – mc9 Sep 17 '15 at 23:54
  • 1
    so many hours lost trying to debug the thing in various ways and it all boiled down to this. having `express.static` as one of the last middleware was breaking everything auth related for me. it wasn't even consistent behaviour, sometimes it worked sometimes it didn't. I suspect something in the express / mentioned libs code is very sketchy and prone to race conditions. – Vee6 Jan 28 '19 at 19:56
13

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.

sanusi
  • 739
  • 7
  • 10
  • 1
    @Squirrl His express server was probably behind a proxy (Heroku, nginx, IISnode) that terminates the SSL connection. That's normal. Read about it here for an= thorough explanation: https://stackoverflow.com/questions/33871133/secure-cookiesession-when-using-iisnode/34599515#34599515 – Christiaan Westerbeek Jan 05 '19 at 07:45
7

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
}))
ufxmeng
  • 2,570
  • 1
  • 13
  • 14
  • Thank you! I spent two hours desperate over this, only to realize that a few months ago I had upgraded cookies to HTTPS and was testing locally over HTTP. – miguelmorin Oct 15 '20 at 10:40
6

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

Smit Patel
  • 1,682
  • 18
  • 23
  • 1
    Thank you! I spent two hours desperate over this, only to realize that a few months ago I had upgraded cookies to HTTPS and was testing locally over HTTP. – miguelmorin Oct 15 '20 at 10:40
4

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.

vinesh
  • 4,745
  • 6
  • 41
  • 45
Adrian Enriquez
  • 8,175
  • 7
  • 45
  • 64
4

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']
}));
ergusto
  • 1,226
  • 2
  • 13
  • 20
3

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' }
Alain1405
  • 2,259
  • 2
  • 21
  • 40
  • 2
    Thank you so much! Suffered with this issue for a while. – Mathyou Dec 24 '19 at 00:53
  • Thank you! I spent two hours desperate over this, only to realize that a few months ago I had upgraded cookies to HTTPS and was testing locally over HTTP. – miguelmorin Oct 15 '20 at 10:40
2

In routes, try adding: {session: true}

app.get('/auto-login', passport.authenticate('cross', {session: true}))
Strawberry
  • 66,024
  • 56
  • 149
  • 197
  • 1
    Just got done spending about an hour debugging passport's internals to come to this same conclusion. `req.user` will not get set if you specify `session: false` in the options of your call to `passport.authenticate`. – Evan Siroky Oct 16 '15 at 06:57
  • `{ session: true }` seems to be default. – flaudre Nov 10 '16 at 01:26
  • Strangely, this worked for me also. Passport v0.3.2. – hyubs Jan 03 '17 at 14:58
  • 1
    I am using `passport-twitter` and setting session explicitly doesn't work for me. In the same app, `passport-google-auth`, which is executing the exact same code lines, is fine. – Old Geezer Mar 02 '17 at 15:57
2

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("/"); });
Federico Grandi
  • 6,785
  • 5
  • 30
  • 50
Mahesh
  • 21
  • 1
2

'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;
1

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();
}};
Mohamed
  • 1,251
  • 3
  • 15
  • 36
1

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

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 })
Honzik
  • 53
  • 4
1

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.

Breakpoint25
  • 1,273
  • 1
  • 9
  • 11
1

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();
  }
});
Sunny Sultan
  • 1,090
  • 15
  • 20
0

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;
}
exclsr
  • 3,329
  • 23
  • 27
0

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

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,
    }));
Luis
  • 1
  • 1
0

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.

Syed Muhammad Asad
  • 208
  • 1
  • 2
  • 13
0

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

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.

jcaruso
  • 950
  • 11
  • 19
0

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");
Alper
  • 1
  • Welcome to Stack Overflow. Code-only answers are discouraged on Stack Overflow because they don't explain how it solves the problem. Please edit your answer to explain how this change fixes the problem in the question, so that it is also useful for other users with the same problem. – FluffyKitten Aug 02 '20 at 05:38
0

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

Make sure you define a cookie option when using session.

This was my issue and took hours for me to figure out as every single example on the internet excludes it.

An example of my code:

app.use(session({
    secret: config.oauth2.secret,
    resave: false,
    saveUninitialized: false,
    cookie : { httpOnly: true, secure : false, maxAge : (4 * 60 * 60 * 1000)},
    store: MongoStore.create({
        mongoUrl: config.mongodb.url,
    })
}));

Don't use httpOnly in production lol.

Colton
  • 1
  • 1
  • Welcome to SO! Although your insight may be valuable and solve the OP issue, this is an old Q&A thread, with an already accepted answer. I found at least 4 others answers almost identical to yours, and already upvoted. Probably yours will not be noticed. or upvoted, it is what we call [Duplicate](https://meta.stackexchange.com/questions/1096/how-should-we-deal-with-duplicate-answers). – Marcelo Scofano Diniz Oct 28 '22 at 13:21
0

My problem was I didn't do the req.login in the post request inside the authenticate. Remember to do the req.login

jack blank
  • 5,073
  • 7
  • 41
  • 73
0

I had this error and it was because my passport.session was not reading its cookieKey and tokenSecret env variables. Go into Heroku app and add process.env.COOKIE_KEY and process.env.TOKEN_SECRET and call them in passport.session

KingJoeffrey
  • 303
  • 5
  • 16
0

I'm encountering the same issue when my site is live in production. In the local environment everything works fine. But when I deployed my site on netlify and try logging in using google strategy or local strategy it says failed to authenticate because req.user is undefined.

I also noticed that from netlify it doesn't set the session cookie when I debugged though developer tools. But the session cookie was successfully created on local host. Following are the code snippets:

Index.js (server)

app.use(cookieSession({
  name: 'session',
  keys: ["MySecretKey"],
  maxAge: 24 * 60 * 60 * 1000,
  secure: false,
  sameSite: 'none'
}))

// Setup Passport

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

The route which gets req.user defined on localhost but undefined on production site:

router.get("/login/success", (req, res) => {
  console.log(req.user);
  if (req.user) {
    res.status(200).json({
      user: req.user,
    });
  } else {
    res.status(401).json({
      success: false,
      message: "User failed to authenticate."
    });
  }
});

Client side request after loading page:

`${import.meta.env.VITE_SERVER_BASE_URL}/auth/login/success`,
{ withCredentials: true }

Any kind of help would be highly appreciated!

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
-1

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