jesusiniesta jesusiniesta - 1 year ago 59
Node.js Question

Why does PassportJS keep redirecting me to failureRedirect?

I'm trying to use PassportJS to protect certain routes of a web application. I'm ussing Passport, ExpressJS, and MongoDB. I find that after an apparently successful authentication, every attempt to access one of these protected routes gets redirected to

again, as if the login had failed.

I've tried making a new project to try and test just this part, and I'm getting the same behaviour: there is an
route, that needs the user to be logged in, and an
one that doesn't. After successfully logging in through the
POST route, the user should be redirected to
; if the logging was unsuccessful, they'd be redirected back to

However, after correctly being redirected to
, the user is redirected back to

screenshoot showing the redirects

What can I possibly be doing wrong here? The user has been successfully logged in, why are they being redirected back to

All the pertinent code is in this GitHub repository. I'll include next the server.js file and the login form:

<form action="/login" method="post">
<label for=login_name>username</label>
<input type="text" name="username"><br/>
<label for=password>password</label>
<input type="password" name="password"><br/>
<input type="submit" value="Go">

This is the Express configuration:

app.set('port', (process.env.PORT || 3004));

app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*'); // Permissive CORS header
res.setHeader('Cache-Control', 'no-cache');
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

app.use(bodyParser.urlencoded({extended: true}));
app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));

Here is the Passport setup:

function verify (username, password, done) {

db.collection("users").findOne({ username: username })
doc => {
if (!doc) {
console.log(`User ${username} doesn't exist`);
done(null, false, { message: "User doesn't exist" });
if (doc.password != password) {
console.log(`${password} is the wrong password`);
done(null, false, { message: "Wrong password" });
else {
done(null, doc);
reason => done(reason)

passport.use(new LocalStrategy(verify));

passport.serializeUser(function(user, done) {
console.log("Serialize here, got " + JSON.stringify(user));
done(null, user._id);
passport.deserializeUser(function(id, done) {
db.collection("users").findOne({ _id: id })
doc => done(null, doc),
reason => done(reason)

And these are my routes:

app.get("/login", (req, res) => {
fs.readFile("./login.html", "utf8", (err, data) => {
if (err) throw err;
})});"/login", passport.authenticate("local", {
failureRedirect: '/login',
}), (req, res) => { res.redirect("/authenticated")});

var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn("/login");
app.get("/authenticated", ensureLoggedIn, (req, res) => res.send("o hai"));
app.get("/unauthenticated", (req, res) => res.send("o hai"));

app.listen(app.get('port'), () => {
console.log('Server started: http://localhost:' + app.get('port') + '/');

Also, these are the versions of each library I'm using:

"dependencies": {
"body-parser": "^1.15.1",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
"cookie-parser": "^1.4.2",
"express": "^4.13.4",
"express-session": "^1.13.0",
"mongodb": "^2.1.19",
"morgan": "^1.7.0",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"path": "^0.12.7"

PD: I'm only using clear-text passwords for this small test, and nowhere near a production DB.

Answer Source

deserializeUser() gets passed the id as a string, but for direct MongoDB queries against _id you need to convert it to an ObjectId first:

passport.deserializeUser(function(id, done) {
  db.collection("users").findOne({ _id: mongodb.ObjectId(id) })
    doc => done(null, doc),
    reason => done(reason)