node.jsmongodbexpresspassport.jsmern

Login route returns "Cannot POST /login" - implementing Passport in MERN


I am trying to implement Passport into my MERN application, registering users and all other routes work but whenever I hit the /login POST route I get Cannot POST /login (see image below). This only happens when I enter a valid username/password (the error paths work fine). Here is the related code: Passport.js

const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");

const initialize = async (passport, getUserByEmail, getUserById) => {
  const authenticateUser = async (email, password, done) => {
    const user = await getUserByEmail(email);
    if (user == null) {
      return done(null, false, {msg: "No user with that email"})
    }

    try {
      console.log(`pass: ${password}, hash: ${user.password}`)
      if (await bcrypt.compare(password, user.password)) {
        console.log(user)
        return done(null, user);
      } else {
        return done(null, false, {msg: "Password incorrect"})
      }
    } catch (e) {
      return done(e)
    }
  }
  passport.use(new LocalStrategy({ usernameField: "email"}, authenticateUser))
  passport.serializeUser((user, done) => done(null, user._id));
  passport.deserializeUser(async (id, done) => done(null, await getUserById(id)));
}

module.exports = initialize

This is the main code I'm having issues with, my routes are:

if (process.env.NODE_ENV !== "production") {
  require("dotenv").config();
}

const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const ItemModel = require('./Models/Item');
const DepartmentModel = require("./Models/Department");
const PeopleModel = require("./Models/Person");
const HouseModel = require('./Models/House');
const ListModel = require('./Models/List');
const bcrypt = require("bcrypt");
const UsersModel = require('./Models/User');
const passport = require("passport");
const flash = require("express-flash");
const session = require("express-session");
const initialize = require("./Passport")
const mongoStore = require('express-session-mongo');

const getUserByEmail = async (email) => {
  return await UsersModel.findOne({email: email});
}

const getUserById = async (id) => {
  return await UsersModel.findById(id);
}


const app = express()

// middleware
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }));
app.use(flash())
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
}))
app.use(passport.initialize())
app.use(passport.session())


// connect to mongoDB
mongoose.connect(process.env.DB)

initialize(passport, getUserByEmail, getUserById)

... omitted unrelated code ...

// ---------------------------- //
// -----   AUTH ROUTES    ----- //
// ---------------------------- //

app.delete("/logout", (req, res) => {
  req.logout((err) => {
    if (err) { return next(err); }
    res.json({ msg: "Logged out"});
  });
});

// !!!!!!LOGIN ROUTE!!!!!!!
app.post("/login", passport.authenticate("local"))

app.post("/register", async (req, res) => {
  try {
    const hashedPassword = await bcrypt.hash(req.body.password, 10)
    await UsersModel.create({
      username: req.body.username,
      password: hashedPassword,
      email: req.body.email
    })
    res.json({ msg: "Registered new user"})
  } catch {
    res.json({ msg: "Error"})
  }
})

const checkAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  } 

  res.json({msg: "Not authenticated"});
}

const checkNotAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return res.json({msg: "Authenticated"});
  } 

  return next();
}

// ---------------------------- //
// -----   START SERVER   ----- //
// ---------------------------- //

app.listen(process.env.PORT, () => {
  console.log("--- Server is UP and running ---")
})

// ---------------------------- //
// ---------------------------- //

[![enter image description here][1]][1]
[1]: https://i.sstatic.net/GPIRNYPQ.png


Solution

  • Here is what I did to fix the issue:

    app.post("/login", passport.authenticate("local"), (req, res) => {
        res.status(200)
        res.send('success')
    });
    

    It needs an additional handler for it work. If the authentication fails, it will automatically respond with a 401 status code.

    You can also directly call passport.authenticate:

    app.post("/login", (req, res, next) => {
      passport.authenticate("local", (err, user, info) => {
        // Handle error (e.g., database error)
        if (err) return next(err);
        // Authentication failed (invalid credentials)
        if (!user)
          return res.status(401).json({ message: "Invalid username or password" });
        // Authentication succeeded
        req.logIn(user, (err) => {
          if (err) return next(err);
          // Redirect or send a success response
          return res.json({ message: "Authentication successful", id: user._id });
        });
      })(req, res, next);
    });
    

    Here is the code I used to reproduce the problem:

    const express = require("express");
    const initialize = require("./initialize");
    const passport = require("passport");
    const session = require("express-session");
    const app = express();
    app.use(express.json());
    app.use(
      session({
        secret: "process.env.SESSION_SECRET",
        resave: false,
        saveUninitialized: false,
      })
    );
    app.use(passport.initialize());
    app.use(passport.session());
    
    const user = {
      _id: "userId",
      password: "passwordThatWorks",
    };
    
    const getUserByEmail = async (email) => {
      return user;
    };
    
    const getUserById = async (id) => {
      return user;
    };
    
    initialize(passport, getUserByEmail, getUserById);
    
    app.post("/login", passport.authenticate("local"), (req, res) => {
        res.status(200)
        res.send('success')
    });
    
    app.listen(4000, () => {
      console.log(`Listening on PORT 4000`);
    });
    
    const LocalStrategy = require("passport-local").Strategy;
    const bcrypt = require("bcrypt");
    
    const initialize = async (passport, getUserByEmail, getUserById) => {
      const authenticateUser = async (email, password, done) => {
        const user = await getUserByEmail(email);
        if (user == null) {
          return done(null, false, { msg: "No user with that email" });
        }
    
        try {
          console.log(`pass: ${password}, hash: ${user.password}`);
        //   if (await bcrypt.compare(password, user.password)) {
          if (true) {
            console.log(user);
            return done(null, user);
          } else {
            return done(null, false, { msg: "Password incorrect" });
          }
        } catch (e) {
          return done(e);
        }
      };
      passport.use(new LocalStrategy({ usernameField: "email" }, authenticateUser));
      passport.serializeUser((user, done) => done(null, user._id));
      passport.deserializeUser(async (id, done) =>
        done(null, await getUserById(id))
      );
    };
    
    module.exports = initialize;