Express-session is not setting cookies in browser
Asked Answered
Q

3

10

So I'm using express-session package to set cookie and session. It's also connected to my MongoDB store to store session. When user logs in, session gets stored in database just fine but there's no cookie in the browser. My app is running in http://localhost:8080/ and my server is running in http://localhost:5500/.

index.js:

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const expressSession = require('express-session');
const mStore = require('./model/database.js').Mstore;
const routes = require('./control/router.js');
const mongooseConnect = require('./model/database.js').mongooseConnect;


app.use(
   expressSession({ 
      secret: 'my secret', 
      resave: false, 
      saveUninitialized: false,
      store: mStore
   }),
   bodyParser.urlencoded({ extended: true }),
   bodyParser.json(),
   routes
);


mongooseConnect(() => app.listen(process.env.PORT || 5500));

router.js:

const express = require('express');
const router = express.Router();
const notesModel = require('../model/database.js').notesModel;
const userModel = require('../model/database.js').userModel;
const cors = require('cors');

router.options('/login', cors());

router.post('/login', cors(), (req, res) => {
   userModel.findOne({ admin_username: req.body.username, admin_password: req.body.password }, (err, data) => {
      if (err) return console.log(err);
      
      if (data) {
         req.session.isLoggedIn = true; // Saves in database just fine.
         res.status(200).json('Login Success'); // This line works just fine as well.
      } else {
         console.log(req.session.isLoggedIn);
         res.status(401).json('Login Failed: Incorrect ID or Password.');
      }
   });
});

Browser: enter image description here

Quartermaster answered 1/11, 2020 at 0:29 Comment(4)
What does it means that your app and your server are running on different ports?Kragh
@Kragh My app is made with Vue.js CLI (default port is 8080) and my server is made with Node.js (port 5500).Quartermaster
Have you found a solution before I give one?Gamecock
@NtshemboHlongwane Yes thank you. I ended up using JWT instead to fix the problem. But I still want to hear about your solution though.Quartermaster
G
21

So before I give my answer I would say JWT is not a safe way of handling sessions

How to handle Express-session

Firstly you will need the following packages

npm i express-session connect-mongodb-session or yarn add express-session connect-mongodb-session

Now that we have packages that we need to setup our mongoStore and express-session middleware:

//Code in server.js/index.js (Depending on your server entry point)
import expressSession from "express-session";
import MongoDBStore from "connect-mongodb-session";
import cors from "cors";
const mongoStore = MongoDBStore(expressSession);

const store = new mongoStore({
  collection: "userSessions",
  uri: process.env.mongoURI,
  expires: 1000,
});
app.use(
  expressSession({
    name: "SESS_NAME",
    secret: "SESS_SECRET",
    store: store,
    saveUninitialized: false,
    resave: false,
    cookie: {
      sameSite: false,
      secure: process.env.NODE_ENV === "production",
      maxAge: 1000,
      httpOnly: true,
    },
  })
);

Now the session middleware is ready but now you have to setup cors to accept your ReactApp so to pass down the cookie and have it set in there by server

//Still you index.js/server.js (Server entry point)

app.use(
  cors({
    origin: "http://localhost:3000",
    methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
    credentials: true,
  })
);

Now our middlewares are all setup now lets look at your login route

router.post('/api/login', (req, res)=>{
    //Do all your logic and now below is how you would send down the cooki

    //Note that "user" is the retrieved user when you were validating in logic
    // So now you want to add user info to cookie so to validate in future
    const sessionUser = {
       id: user._id,
       username: user.username,
       email: user.email,
    };
    //Saving the info req session and this will automatically save in your     mongoDB as configured up in sever.js(Server entry point)
    request.session.user = sessionUser;

    //Now we send down the session cookie to client
    response.send(request.session.sessionID);

})

Now our server is ready but now we have to fix how we make request in client so that this flow can work 100%:

Code below: React App/ whatever fron-tend that your using where you handling logging in

//So you will have all your form logic and validation and below
//You will have a function that will send request to server 

const login = () => {
    const data = new FormData();
    data.append("username", username);
    data.append("password", password);

    axios.post("http://localhost:5000/api/user-login", data, {
      withCredentials: true, // Now this is was the missing piece in the client side 
    });
};

Now with all this you have now server sessions cookies as httpOnly

Gamecock answered 9/11, 2020 at 18:44 Comment(4)
Thank you for this comprehensive answer!Quartermaster
Where does response (as in response.send(request.session.sessionID)) come from?Bourguiba
Probably just a typo res/req @BourguibaSomewhat
This answer is goated !!Reprehend
B
6

I encountered this issue and after trying much of what has already been highlighted here, none still worked for me. However, after digging further, I realized that when running on your localhost. The secure property of your cookie object should always be set to false The below code snippet from my express app solved it for me.

app.use(
    session({
        store: new RedisStore({ client: redisClient}),
        name: 'qid',
        secret: 'superdupersecret',
        resave: false,
        saveUninitialized: false,
        cookie: {
            httpOnly: true,
            secure: false,
            maxAge: 1000 * 60 * 60 * 24 * 365
        }
    })
);
The main change to note is secure:false
Blubber answered 29/10, 2022 at 17:35 Comment(2)
This only works if your client and server are on the same domainBedight
this worked for me. such a stupid mistake i can't believe i couldn't find it for so longOversold
C
0

The most common reason why this doesn't work is because you're not attaching credentials in the request. when making any HTTP request to a server that sets cookies in your browser you need to add

credentials: 'include'

fetch(url, {
  credentials: 'include',
  method: 'post',
})
.then(response => {//do work});

If you're using axios, you need to do a similar thing

axios.create({ baseURL: serverUrl, withCredentials: true });
Carbonic answered 30/6, 2023 at 17:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.