Search code examples
javascriptnode.jsreactjsexpressexpress-session

My express-session is getting overwritten every time I send a new request, and I'm wondering if it is because of my dev environment? REACT + EXPRESS


EDIT - I no longer think it is a development environment issue as the same thing persists in production.

First off, appreciate anytime spent on this - I know that this is a commonly asked question, but I've perused so many questions and tried applying so many of the solutions to no avail.

To summarize, I currently have a React Front-end that is sending request with the Fetch API to my Express server. I want to use express-sessions to save my logged in user information for the session to use and authenticate as the user uses the application.

I'm having this weird issue where every time a new request is sent, the cookie in my browser in the application tab gets set to something completely new. I might be understanding this wrong, but I thought that that was supposed to remain consistent throughout the session, but it doesn't seem to be the case. This is making it so that every time I call a new request, req.session is completely reset with nothing.

Here is some code from my application:

Express- server.js [my setup for express-session and cors]

app.use(cors({
  credentials: true
}))

const session = require('express-session')
// Middleware for creating sessions and session cookies.
// A session is created on every request
app.use(session({
  secret: 'tis a secret mate',
  cookie: {
    expires: 3000, // expires in 15 mins
    httpOnly: true,
    secure: false,
  },
  // Session saving options
  saveUnintialized: false, // don't save the initial session if the session object is unmodified (i.e the user did not log in)
  resave: true, // don't resave a session that hasn't been modified
  store: MongoStore.create({
    mongoUrl: process.env.MONGODB_URI_SESSIONS
  })

  }))

  app.use(function (req, res, next) {
    console.log('SESSION LOGGING MIDDLEWARE')
    console.log(req.session);
    next()
  });

app.use((req, res, next) => {
  res.header('Access-control-Allow-Origin', 'http://localhost:3000');
  res.header(
      "Access-Control-Allow-Headers",
      "Origin, X-Requested-With, Content-Type, Accept, Authorization"
  );
  res.header('Access-Control-Allow-Credentials', true);
  next();
  });

EDIT - Login Route

app.post('/api/users/login', async (req, res) => {

  // pull out the body
  const body = req.body

  // pull out the username and password
  const username = body.username
  const password = body.password

  User.findByUsernamePassword(username, password)
    .then(user => {
      // if we find the user,  the users information to the session to retain all that information
      // will create a function to ensuree that the session exists to make sure that we are logged in
      req.session.user = user
      req.session.username = user.username
      req.session.save()
      console.log(req.session)
      res.send({ user: req.session.user })
    })
    .catch(error => {
      res.status(400).send()
    });
})

Front-end Fetch Calls In the code below, I pasted my login and check-session calls to their respective endpoints. Essentially the check session call just returns what is in the session, and the login call is supposed to login the user and add the user to req.session.user

After logging in, I try to run check-session to see if the user is in the session, but it is not there every time. I also notice that the cookie in my applications tab for chrome changes to something else.

export const login = (username, password, setUser) => {

  const obj = {
    username: username,
    password: password
  }

  const request = new Request(`${API_HOST}/api/users/login`, {
    method: "POST",
    credentials: "include",
    body: JSON.stringify(obj),
    headers: {
      "Content-Type": "application/json"
    }
  })

  // send the request
  const promise = fetch(request)
    .then(res => {
      if (res.status === 200) {
        return res.json();
      }
    })
    .then(json => {
      // if the user exists, setUser to user
      console.log("USER PRE:", json.user)
      if (json.user !== undefined) {
        console.log("USER: ", json.user)
        setUser(json.user)
        return json.user
      }
    })
    .catch(error => {
      console.log(error);
    })

  return promise
}

export const checkSession = () => {
  const url = `${API_HOST}/api/users/check-session`

  fetch(url, {
    credentials: 'include'
  })
  .then(res => {
      if (res.status === 200) {
          console.log('200')
          console.log(res.json())
      } else {
        console.log(res)
      }
  })
  // .then(json => {
  //     if (json) {
  //       setUser(json)
  //     }
  // })
  .catch(error => {
      console.log(error);
  });
}

Things I've Tried

  • I've tried changing things to how my cookie is defined in server.js (secure: false | httpOnly: true)
  • I've tried adding credentials to my fetch calls and accepting them in cors in server.js
  • I changed my chrome to disable-web-security
  • I added cors access control stuff to the response header as seen above

Additional notes that might be helpful

  • I'm using React with React Router, not sure if that would affect stuff

Thank you for your time, I know this is a super long question. Please let me know if there is anything else I could provide to help clarify if you want to help!


Solution

  • If you're running the backend and frontend on different hosts, there could be a CORS problem. Also you said that you checked the cookie in the application tab, but if you haven't tried looking at the request and response cookie headers in the network tab, you should check those out to further diagnose the problem. The cookie should be set on the authentication request, and it should be included in the requests following that.