Express-session not storing cookies

Hi everyone :hugs:

I am struggling with a task on my e-commerce project: Enable session support. More specifically, it seems that I can’t set cookies (the session ID) using express-session. I am expecting to see the ID on Chrome’s DevTools → Application → Cookies but there is none.

Let me know what do you think I do wrong
Any suggestion is welcomed and appreciated!

Here is my Express server file, server.js:

//Project Codecademy link: 
//https://www.codecademy.com/paths/full-stack-engineer-career-path/tracks/fscp-22-portfolio-project-e-commerce-app-rest-api/modules/fscp-22-e-commerce-app-rest-api/kanban_projects/ecommerce-app-rest-api 
const express = require('express');
const cors = require('cors');
const session = require('express-session');
var path = require('path');
const store = new session.MemoryStore(); //Note: Storing in-memory sessions is something that should be done only during development, NOT during production due to security risks.
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
// Passport-local docs: http://www.passportjs.org/howtos/password/

const bodyParser = require('body-parser');
//body parser Docs: https://expressjs.com/en/resources/middleware/body-parser.html
const app = express();
const port = 3001;
var swaggerJSDoc = require('swagger-jsdoc');

// swagger definition
var swaggerDefinition = {}
//options for swagger docs
var options = {}
// initialize swagger-jsdoc
var swaggerSpec = swaggerJSDoc(options);
app.use(express.static(path.join(__dirname, 'public')));

//Import Database utils
const db = require('./db/index');

const corsOptions = {
  origin: 'http://localhost:3001', 
  credentials: true,
};
app.use(cors(corsOptions));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(session({
    secret: "f4z4gs$Gcg", //should be storred securelly in a .env variable
    cookie: { path: '/', httpOnly: true, secure: false, maxAge: null },
    saveUninitialized: true,
    resave: false,
    store,
  })
);

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  db.users.findById(id, function (err, user) {
    if (err) {
      return done(err);
    }
    done(null, user);
  });
});

app.post("/login",  db.loginUser);

//------------------------------------------------------------- Products "CRUD"
app.get('/products/', db.getProdByHeigth); //I should check if the body has the heigth, if not, this should return all products
app.get('/products/:id', db.getProdById);

//------------------------------------------------------------- User "CRUD"
app.get('/users/', db.getUsers);
app.get('/users/:id', db.getUserById);
app.post('/users/register', db.registerUser);
app.delete('/users/:id', db.deleteUser);

//------------------------------------------------------------- Cart "CRUD"
app.get('/cart/:id', db.getCartById);
//Need to add one get for the products of a cart
app.post('/cart', db.postCart);
app.delete('/cart/:id', db.deleteCart);
app.post('/cart/:id/checkout', db.cartCheckout);

//------------------------------------------------------------- Orders "CRUD"
app.get('/orders', db.getOrders);
app.get('/orders/:id', db.getOrderById);
app.get('/orders/:id/products', db.getOrderProducts);


app.get('/swagger.json', function(req, res) {
  res.setHeader('Content-Type', 'application/json');
  res.send(swaggerSpec);
})

app.listen(port, () => {
    console.log(`Listening on post ${port}.`);
})

Here is the user login middleware, loginUser:

const loginUser = (req, res) => {
    const { email, password } = req.body;
    getUserByEmail(email, (err, user) => {
        if (!user) return res.status(403).json({ msg: "User not found!"})

        //Compare hash 
        const passMatch = bcrypt.compareSync(password, user.password);
        //If match, store the user in the session
        if (passMatch){
            req.session.isAuthenticated = true;
            req.session.user = {
            email,
            password,
            first_name: user.first_name,
            last_name: user.last_name,
            };
            res.status(200).json({ status: 200, msg: "Succesful"});
        } else {
            res.status(403).json({ msg: "Bad Credentials"});
        }
    })
}

Hey @bogdan.marius14 ,

This issue is very common when dealing with this package! It’s super frustrating, because if you don’t configure your response headers correctly the browser won’t save the session in the cookie. This is kind of a catch-22 because in a way you are dependent on the browser keeping this cookie secure, but you have to make sure you set it up correctly.

I see you provided your express app. Do you mind providing the entire repo?

Michael

Yes @mdwiltfong , I can share the repo. Here it is

Thanks for helping !

1 Like

Hey @bogdan.marius14 ,

Thank you for sharing that. I’ve made a PR to address some issues I saw. One of which was the CORs issue.

So after reviewing your code, it’s kind of hard to deduce what the issue is. I see you are using MemoryStore. Since you have a database setup, I would not use this. You should instead try to configure your session middleware to connect to your DB. Have you tried taking a look at this package?

Looking through my notes here is an example of how you can implement it:

const express = require("express"); 
const app = express(); 
// Import the passport library below: 
const passport = require("passport"); 
// Import the passport-local library below: 
const LocalStrategy = require("passport-local").Strategy; 
const session = require("express-session"); 
const PORT = process.env.PORT || 5000; 
const pool = new pg.Pool({ 
  user: process.env.DATABASE_USER, 
  host: process.env.DATABASE_HOST, 
  port: process.env.DATABASE_PORT, 
  password: process.env.DATABASE_PW, 
  database: "bandnameapi", 
});
app.use( 
  session({ 
    secret: "secret-key", 
    resave: false, 
    cookie: { maxAge: 1000 * 60 * 60 * 24, secure: true, sameSite: "none" }, 
    saveUninitialized: false, 
    store: new pgSession({ 
      pool: pool, 
      tableName: "sessions", 
    }), 
  }) 
);

Final note, you should maybe create a script that will help contributors to create the database as well as any tables the app needs. There’s a couple ways to do this.

You could write .sql files with raw sql queries, and then instruct the contributor through the readme on how to execute these scripts, or since you are using an npm package to execute raw sql scripts already, you can create a .js file and then make it executable as an NPM scriipt.

Here’s an example of writing .sql files, and here’s another where a fellow codecademy student made a .js file.

The reason I bring this up, is because without knowing the schema (as in, the structure of your tables and what data it takes), I can’t manually test your app on my machine. So I can only guess that using a connector to your PSQL data base might help solve the issue.

Keep up the great work!

1 Like

Thank you @mdwiltfong for the advice and for contribution.
However, I have some more questions, if you do not mind:

  1. Since it is difficult to use and debug express-session (as I can notice) can you suggest any other way to implement sessions (and use cookies), perhaps a different package?
  2. In the PR I have noticed you have changed ' with ". Is there a particular reason for it?

Thanks,
Bogdan.

Hey @bogdan.marius14 ,

No worries! This is a lengthy project.

I’ll answer your questions in the order you sent them.

  1. Express-session seems to be very popular. I can’t even find a replacement/competitor. Nonetheless, I don’t think express-session is the problem, but rather setting up your app in a way that the browser will co-operate with you. So regardless of the package you may encounter this issue. Just stick to it, and keep posting your problems here!

  2. Oh I should have stuck with the choice of " versus ' . In this case there is no particular reason.

Michael

1 Like

Hi there. I have an update.

I have managed to use a different approach towards my aim of using cookies and sessions. As I have taken some inspiration from Islem and his video, Add Login/Auth to your React app in 5 mins, I have managed to do this:

Basically, I am using passport and passport-jwt for the server-side and react-auth-kit for the client-side.
The whole code can be found in the repo

Any thoughts? Thanks!

Nice!

Good job trying to find a work around. I’ll check out your solution soon.

Michael