Passport js "isAuthenticated()" doesn't work as expected. But it does work with postman

36 Views Asked by At

So, I have a simple Node Express server and I am handling authentication with Passport.js. I am struggling when trying to make it work correctly.

Here's the setup:

  • I have a React frontend where users can input their email and password in a form. (Shouldn't be the problem, as I tried to make the same POST request from an html and script file, got the same error).

  • Upon form submission, the application should send a POST request to the server (http://localhost:8080/login) with the email and password data.

  • The frontend application is using Axios to send the POST request to the backend. (Tried fetch, same response).

  • The server should:

    • Receive email and password.

    • Verify these credentials are OK.

    • Serialize/deserialize user.

    • Redirect to "/auth/jwt" which is protected by passportAuth middleware. ("/auth/jwt" is not implemented yet, so this route just gets de req and sends a 200 status, it does nothing else).

    • And here remains the problem. In the passportAuth middleware, which is essentialy, "req.isAuthenticated()", returning false UNLESS I send the email and password from postman, which makes everything very frustrating.

    • Sessions are getting stored in MongoDB and the key "passport" does exist with user's _id, as expected, but I do not know why isn't it working.

  • My thoughts:

    • Shoudn't be cors problem, as I set app.use(cors()).

    • Client request should be fine.

    • Web app and postman's request are the same, just email and password.

    • Headers problem? Tried different configs, still not working.

React Frontend:

...
const userData = { email, password };
const config = { headers: { "content-type": "application/json" }};
const res = await axios.post("http://localhost:8080/login", userData, config);
console.log(res);
...

Server.js

const initializePassport = require("./passport/local");

const sessionConfig = {
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: false, // Resave and saveUninitialized: tried false and true.
    cookie: {
      secure: false,
      httpOnly: false,
    },

    store: new mongoStore({
      mongoUrl: URI_CLOUD_CONNECTION,
      ttl: 60 * 30,
      expires: 60 * 30,
      autoRemove: "native",
    }),
  }

  try {
    await mongoose.connect(URI_CLOUD_CONNECTION);

    initializePassport(passport); // local.js
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use(cookieParser(SESSION_SECRET));
    app.use(cors());
    app.use(expressSession(sessionConfig)); // express-session

    // Passport initialization
    app.use(passport.initialize());
    app.use(passport.session());

Login route:

// "/login"
router.route("/").post(PassportController.login);

PassportController:

const passport = require("passport");

const login = passport.authenticate("login", {
  successRedirect: "/auth/jwt",
})

"/auth/jwt" route:

const passportAuth = require("../../middlewares/passportAuth");
const JwtController = require("../../controllers/authControllers/jwt-controller");

// "/auth/jwt"
router.get("/", passportAuth, JwtController.generateTokenAndRedirect); // This JwtController just sends status 200.

PassportAuth middleware:

module.exports = (req, res, next) => {
  if (!req.isAuthenticated()) {
    logger.error("User not authenticated by passport!");
    return res.sendStatus(401);
  }

  console.log("Passport ID en session: ", req.session.passport);
  logger.info("PassportAuth authenticated.");

  next();
};

Local.js (passport main logic):

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

module.exports = (passport) => {
  const authenticateUser = async (req, email, password, done) => {
    try {
      const user = await userModel.getUserByEmail(email);

      if (!user) {
        const errorMessage = { message: "Requested user does not exist." };
        return done(errorMessage, false);
      }

      const isPasswordValid = await userModel.isPasswordValid(email, password);

      if (!isPasswordValid) {
        const errorMessage = { message: "Wrong password." };
        return done(errorMessage, false);
      }

      done(null, user); // This works just fine.
    } catch (error) {
      logger.error("Error in local.js.", error?.message);
      return done(error);
    }
  };


  passport.use("login", new LocalStrategy({
    usernameField: "email",
    passwordField: "password",
    passReqToCallback: true,
  }, authenticateUser));


  passport.serializeUser((user, done) => {
    console.log("Serializing user (only _id)...", user._id.toString());
    return done(null, user._id.toString());
  });

  passport.deserializeUser(async (_id, done) => {
    console.log("Deserialize userId: ", _id);
    const user = await userModel.findById(_id);
    const userData = {
      _id: user._id.toString(),
      email: user.email,
      firstname: user.firstname,
      lastname: user.lastname,
    }
    
    done(null, userData);
  });
};

It is in passportAuth "isAuthenticated()" where I have this issue with postman and web app. When using postman, it does work and execute next() function. But when requesting from client, it doesn't.
I am losing my mind as I do not understand what is the issue.

Steps I console.logged:

When hiting login:

  • Email and password are valid.

  • Serializing user... (_id)

  • Error User is not authenticated by passport!

When trying with postman...

  • Email and password are valid.

  • Serializing user... (_id)

  • Deserializing user... (same _id)

  • Passport ID in session = { user: "(_id number)" }

  • Passport authenticated

  • Sending status 200 OK.

0

There are 0 best solutions below