Search code examples
javascriptnode.jsreactjsexpressexpress-session

express-session won't persist and won't send a cookie to the browser


I am working on a mern stack (react, node, express & mongodb) web app. I have installed express-session on node.js. however, i don't see a connect.sid cookie in the browser. also, it seems that the session does not persist between requests in node.

I initially thought that this was a cors problem (which could still be the case) so I tried some tweaking on the CORS headers, but without any luck.

//this is the main app.js file in node.js

var session = require('express-session')

app.use((req, res, next) => {
    res.header('Access-control-Allow-Origin', '*');
    res.header(
        "Access-Control-Allow-Headers",
        "Origin, X-Requested-With, Content-Type, Accept, Authorization"
    );
    res.header('Access-Control-Allow-Credentials', true);
    if (req.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
        return res.status(200).json({});
    }
    next();
});

app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: false }
}));

//this is the index.js route file in node.js

router.get('/check_if_session_exists_when_refreshing', async (req, res, next) => {
  try {
    res.json(req.session)
  }
  catch (err) {
    console.log(err);
  }
});

router.post('/login', function (req, res, next) {
  UserModel.findOne({ username: req.body.username }).then((data) => {
    bcrypt.compare(req.body.password.toString(), data.password.toString()).then((resp, err) => {
      if (resp) {
        req.session.user = {
          username: req.body.username,
          password: req.body.password
        }
        res.json([data])
      }
      else console.log(err)
    })
  });
});
// this is the React-Redux login action on the client side
import { FETCH_USER } from './types';

export const fetchUser = (userData) => dispatch => {
    fetch("http://localhost:3000/login", {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(userData)
    }).then(response => response.json())
        .then(user =>
            dispatch({
                type: FETCH_USER,
                payload: user
            })
        );
};

The expected result: A persisting session id on the express framework and a cookie file stored in the browser.

The actual result: Session does not persist and cookie is not stored.


Solution

  • FIX UPDATE: the problem was that the credentials init options weren't set on the fetch API.

    the correct code should be written like this:

    // this is the React-Redux login action on the client side
    
    export const fetchUser = (userData) => dispatch => {
    fetch("http://localhost:3000/login", {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        credentials: "include",
        body: JSON.stringify(userData)
    }).then(response => response.json())
        .then(user =>
            dispatch({
                type: FETCH_USER,
                payload: user
            })
        );
    };
    

    also, in the CORS settings, a wildcard ('*') cannot be used as the 'Access-Control-Allow-Origin'. Instead, it needs the origin address which in my case was http://localhost:3001.

    //this is the main app.js file in node.js
    
    app.use((req, res, next) => {
    res.header('Access-control-Allow-Origin', 'http://localhost:3001');
    res.header(
        "Access-Control-Allow-Headers",
        "Origin, X-Requested-With, Content-Type, Accept, Authorization"
    );
    res.header('Access-Control-Allow-Credentials', true);
    if (req.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
        return res.status(200).json({});
    }
    next();
    });
    

    In retrospect, I have already figured this out in a relatively early stage. but unbeknownst to me, resetting nodemon wasen't enough after making those changes. a manual server shutdown was needed for the changes to take place.