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