Session custom property getting undefined when calling Node js API from Javascript fetch

21 Views Asked by At

I am developing a User Login Project where Apis are written in node js and express, UI part is written in HTML and Javascript. These both are two individual projects.

  1. When User logins, a fetch cal is made in javascript which hits the '/api/login' URL in Node Js.
  2. If user credentials are valid user object is set in session provided by express-session package. req.session.user = userDetails;
  3. And in UI on fetch call success, the page redirects to Dashboard.html.
  4. On rendering Dashboard.html an another api call is made to check the session validity which hits the 'api/dashboard' URL in Node Js.
  5. Here when I try to get the user object from session, getting undefined.
if (req.session.user) {
        res.status(200).json({ message: 'Authenticated', user: req.session.user });
     } else {
        res.status(401).json({ error: 'Unauthorized' });
     }

  1. Thus getting unauthorized and redirecting back to login page.

Note: I am running the UI project using GoLive extension in Visual Studio Code

API Project files:

server.js

const express = require("express");
    const mongoose = require("mongoose");
    const cors = require("cors");
    const cookieParser = require("cookie-parser");
    const session = require("express-session");
    const { config } = require("dotenv");
    config();
    const registerRouter = require("./routes/register");
    const loginRouter = require("./routes/login");

    const app = express();
    app.use(cors({ origin: "http://XXX.X.X.X:5500", credentials: true }));
    app.use(express.json());
    app.use(cookieParser());
    app.use(
      session({
        secret: "This is a secret",
        resave: false,
        saveUninitialized: true,
        cookie: { maxAge: 1 * 60 * 1000, httpOnly: true },
      })
    );

    mongoose.connect(process.env.ATLAS_URL, {
      useNewUrlParser: true,
    });

    const db = mongoose.connection;
    db.on("error", (error) => console.log(error));
    db.once("open", () => console.log("Connected"));

    app.use("/api", registerRouter);
    app.use("/api", loginRouter);

    app.listen(process.env.PORT, () => {
      console.log("app is listening on Port " + process.env.PORT);
    });

login.js

const express = require("express");
    const User = require("../models/User");

    const router = express.Router();

    router.post("/login", async (req, res) => {
      try {
        const user = await User.findOne({
          email: req.body.email,
          password: req.body.password,
        });
        if (user == null)
          res.status(401).json({ error: "Invalid email or password" });
        else {
          let userSession = {
            firstName: user.firstName,
            lastName: user.lastName,
            email: user.email,
          };
          if (req.session.user) {
              res.status(200).json({ message: "User login Successful" });
          } else {
            req.session.user = userSession;
            // console.log(req.session);
            // res.cookie('user',userSession);
            res
              .status(200)
              .json({ message: "User login Successful for first time" });
          }
        }
      } catch (error) {
        console.log(error);
        res.status(500).send(error);
      }
    });

    router.get("/logout", (req, res) => {
      req.session.destroy();
      res.status(200).json({ message: "Logout successfull" });
    });

    router.get("/dashboard", (req, res) => {
      console.log("hit");
      console.log(req.session);
      console.log(req.session.user);
      if (req.session.user) {
        res.status(200).json({ message: "Authenticated", user: req.session.user });
      } else {
        res.status(401).json({ error: "Unauthorized" });
      }
    });

module.exports = router;

UI Project Files:

ui-login.js

var loginUrl = "http://localhost:3000/api/login";

    $(document).ready(function () {
        $('#loginForm').submit(()=>{
            var email = $('#email').val();
            var password = $('#password').val();

            if(email.trim() == "") {$('#emailError').show(); return false;} else       $('#emailError').hide();
            if(password.trim() == "") {$('#passwordError').show();  return false;} else     $('#passwordError').hide();

            const data = {
                email: email,
                password: password
            };

            const requestOptions = {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data),
                credentials: 'include'
            };

            fetch(loginUrl, requestOptions)
                .then(response => {
                    if(!response.ok){
                        if(response.status == 400){
                            $('#loginError').text(response);
                            $('#loginError').show();
                        }else{
                           throw new Error("User Login failed!");
                        }
                    }
                    return response.json();
                })
                .then((data)=>{
                    $('#loginError').hide();
                    alert(data.message);
                    window.location.href = '/pages/dashboard.html';
                    return false;
                })
                .catch(error =>{
                    console.log(error);
                });
            return false;
        });
    });

ui-dashboard.js

var dashboardUrl = "http://localhost:3000/api/dashboard";

    const checkSession = () => {
      fetch(dashboardUrl, {
        method: "GET",
        credentials: "include"
      })
        .then((response) => {
          if (response.status != 200) {
            alert("Unauthorized. Redirecting to login page.");
            window.location.href = "/pages/login.html";
          }
        })
        .catch((error) => {
          console.error("Error:", error);
          alert("Error checking session");
          window.location.href = "/pages/login.html";
        });
    };

    checkSession();

In Network call, getting session id in response header but not getting user object I had set in session. Login Network call

In Dashboard network call, no session is sent in request header. Dashboard Network call

Exhausted figuring out the issue. Thanks in Advance!

I tried many things like setting credentials to true in api calls. Setting cors in Nodejs. Retrieving set-cookie from response header in JavaScript. Tried many online references. At last also tried ChatGPT.

But could not sort out the issue.

Am I in right direction?

1

There are 1 best solutions below

0
Revanth Sai On

Finally fixed it. JavaScript and browser needs to be blamed. After some research got to know that browser won't send cookies to different domains even after setting 'credentials' : 'include' in fetch headers. Thus only way to fix it is to run both UI and API in same domain. I tried running both UI and API in same domain i.e. localhost but different port numbers. That's it issue got fixed.

Reference helped to find out the solution. Credentials: 'include' not including Cookie header