Jk041
Jk041

Reputation: 982

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 /login 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 /authenticated route, that needs the user to be logged in, and an /unauthenticated one that doesn't. After successfully logging in through the /login POST route, the user should be redirected to /authenticated; if the logging was unsuccessful, they'd be redirected back to /login.

However, after correctly being redirected to /authenticated, the user is redirected back to /login!

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 failureRedirect?

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">
</form>

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

app.use(morgan('combined'));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());

Here is the Passport setup:

function verify (username, password, done) {

  db.collection("users").findOne({ username: username })
  .then(
    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 { 
        console.log("AOK"); 
        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 })
  .then(
    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;
    res.send(data);
})});

app.post("/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.

Upvotes: 0

Views: 535

Answers (1)

robertklep
robertklep

Reputation: 203286

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) })
  .then(
    doc => done(null, doc),
    reason => done(reason)
  );
});

Upvotes: 1

Related Questions