Node + Express + Passport: req.user 未定义

我的问题类似于这个 ,但是没有洞察到他的解决方案。

我用护照在 Instagram 上认证。成功的身份验证之后,用户被引导到“/”。此时,请求具有用户对象(也就是它正在工作)。但是,一旦重定向,req.user 就是未定义的。:'(

奇怪的是,每个请求都会调用 passport.derializeUser。它成功地获取了用户对象,但是在中间件过程中,req.user 没有被设置(或者没有被取消设置)。

// 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
}
109608 次浏览

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

In routes, try adding: {session: true}

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

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

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.

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

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

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

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

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.

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

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

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

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

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

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

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.

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("/"); });

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.

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

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' }

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

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 🍻

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.

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

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

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

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.

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

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