can't get cookies to work. Node/React/Vercel/Render

clock icon

Asked 9 months ago

message

0 Answers

eye

1 Views

0

I did not write the backend, and do not have backend experience. It was 2 man project and now I am trying to gain that experience by deploying it.
I have backend deployed on Render, frontend on Vercel.
Everything worked in development, and in preview as well. It was only after deploying, that the cookie based issues appeared, meaning to test the code I have to keep redeploying as they won't appear in testing.

const path = require('path');

const      express = require('express');
const   bodyParser = require('body-parser');
const     mongoose = require('mongoose');
const      session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const       socket = require('socket.io');
const       multer = require('multer');
const         cors = require('cors');
require('dotenv').config();

const User = require('./models/user');

const MONGODB_URI = process.env.MONGO_KEY;
const    app = express();
const   http = require('http');
const server = http.createServer(app);
const     io = socket(server);

let users = [];

const addUser = (userId, socketId) => {
  !users.some((user) => user.userId === userId) && users.push({ userId, socketId });
};

const removeUser = (socketId) => {
  users = users.filter((user) => user.socketId !== socketId);
};

const getUser = (userId) => {
  return users.find((user) => user.userId === userId);
};

io.on('connection', (socket) => {
  // when connect
  console.log('a user connected.');

  // take userId and socketId from user
  socket.on('addUser', (userId) => {
    addUser(userId, socket.id);
    io.emit('getUsers', users);
  });

  // send and get message
  socket.on('sendMessage', ({ senderId, receiverId, text }) => {
    const user = getUser(receiverId);
    io.to(user.socketId).emit('getMessage', {
      senderId,
      text,
    });
  });

  // when disconnect
  socket.on('disconnect', () => {
    console.log('a user disconnected!');
    removeUser(socket.id);
    io.emit('getUsers', users);
  });
});

const store = new MongoDBStore({
         uri: MONGODB_URI,
  collection: 'sessions',
});

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'images');
  },
  filename: (req, file, cb) => {
    cb(null, new Date().toISOString().replace(/:/g, '-') + '-' + file.originalname);
  },
});

const fileFilter = (req, file, cb) => {
  if (
    file.mimetype === 'image/png' ||
    file.mimetype === 'image/jpg' ||
    file.mimetype === 'image/jpeg'
  ) {
    cb(null, true);
  } else {
    cb(null, false);
  }
};

app.set('views', 'views');
app.set('view engine', 'ejs');

const  marketRoutes = require('./routes/market');
const   adminRoutes = require('./routes/admin');
const    authRoutes = require('./routes/auth');
const messageRoutes = require('./routes/message');
const    chatRoutes = require('./routes/chat');
const          user = require('./models/user');

app.use(
  cors({
         origin: process.env.CLIENT_URL, // configured to accept credentials from front end for session cookies to work
    credentials: true,
  })
);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(multer({ storage: storage, fileFilter: fileFilter }).single('image'));
app.use(express.static(path.join(__dirname, '/public')));
app.use('/images', express.static(path.join(__dirname, 'images')));
app.use(
  session({
               secret: 'my secret',
               resave: false,
    saveUninitialized: false,
                store: store,
               cookie: {
                     maxAge: 30 * 24 * 60 * 60 * 1000,
                   httpOnly: true,   
                     secure: true,  
                   sameSite: 'None', 
              },
  })
);

app.use((req, res, next) => {
  res.locals.isAuthenticated = req.session.isLoggedIn;
  next();
});

app.use((req, res, next) => {
  if (!req.session.user) {
    return next();
  }
  User.findById(req.session.user._id)
    .then((user) => {
      if (!user) {
        return next();
      }
      req.user = user;
      next();
    })
    .catch((err) => {
      console.log(err);
    });
});

app.use(marketRoutes);
app.use(adminRoutes);
app.use(authRoutes);
app.use(messageRoutes);
app.use(chatRoutes);

mongoose
  .connect(MONGODB_URI)
  .then((result) => {
    server.listen(3000);
    console.log('connected!');
  })
  .catch((err) => {
    console.log(err);
  });

So after going round in circles with several AIs, they keep repeating the same thing, to add these lines that I did not originally have:

                   httpOnly: true,   
                     secure: true,  
                   sameSite: 'None', 

This hasn't worked. Without these lines, the app did have full functionality only in Firefox browser. Any other browser I tested it on, you can see the DB data, you can signup/login (POST requests), but you cannot perform any other request, because the controller will check for a req.session.user in pretty much every controller. This relies on cookies to work correctly. The client side will use some hooks to make requests which will call this function

export interface Fetch {
  params: string;
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
   data?: object;
}

export const fetchData = async ({ params, method, data }: Fetch) => {
  const isFormData = data instanceof FormData;
  const body = data ? (isFormData ? data : JSON.stringify(data)) : null;

  const response = await fetch(import.meta.env.VITE_SERVER_URL + params, {
    method,
    headers: isFormData ? undefined : { 'Content-Type': 'application/json' },
    credentials: 'include', // send cookies to backend
    body,
  });

  const resData = await response.json();

  if (!response.ok) {
    throw resData;
  }

  return resData;
};

The Cors is set to receive credentials. Credentials set to true on Client side. Everything works in Dev/Preview and Firefox with the App code just like this:

app.use(
  session({
               secret: 'my secret',
               resave: false,
    saveUninitialized: false,
                store: store,
               cookie: {
                   maxAge: 30 * 24 * 60 * 60 * 1000, 
              },
  })
);

But the cookies will not work in any non Firefox browser. Then adding the extra cookie config prevents it from working in any environment at all.

I have tried amending the cookie config and redeploying but it only made it less functional. I would have thought the set up is correct but I am not able to receive any further help on this. Right now I could deploy it without the cookie config and just leave a note that this only has full functionality in Firefox.

0 Answers

Write your answer here

Top Questions