Karthikey Saxena
Karthikey Saxena

Reputation: 59

Not able to receive/set cookies in browser from backend in MERN app with backend hosted on heroku and frontend on netlify

I have a MERN app whose backend is hosted on Heroku and frontend on netlify. I am not able to see the cookies in the browser after deploying the app but it works fine on localhost. I think it is due to different backend and frontend ports, what am I missing, please help

Upvotes: 4

Views: 5139

Answers (2)

Satya
Satya

Reputation: 31

I was facing exactly the same problem. The problem was that cookies are not being set in the browser after successful login/registration. I need that session id set in the cookie to check user authentication on every CRUD/RESTful API call.

As cookies are not set, my API request call does not include it and the user is not authenticated though having logged in successfully. So the user can not access the page that requires session-based authentication. Which of course my entire app relies on.

The thing is that we are using a different domain for both the backend and the front end. By default chrome or other browser does not set cookies for a website(front end) that makes API calls hosted on another domain name for security reason. I do not know how exactly these browsers stop setting cookies but if we do not specify some options they will do for sure.

This works fine when we use http://localhost:3000 for react app and http://localhost:7000 for the backend. But problems come when we deploy on the cloud. There are a couple of things I did that helped me to address this issue. I will illustrate in simple steps specifically on that exact problem part assuming you have done other stuff correctly.

Here are the steps that worked for me. SEE these arrow ⬅️⬅️ marks where I am pointing/ referring to.

TO DO IN FRONTEND REACT APP (step 1 -2)

STEP1: Setup proxy in package.json file in react app. This tells ur React app to include this proxy url before any incomplete URL like "/update/:noteid". If an URL contains something like http then the proxy will not set the default proxy value.

`"version": "0.1.0", 
  "private": true,
  "proxy": "https://your-backend.com", ⬅️⬅️
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",`

STEP2: Add "credentials" : "include" API fetch like below

`fetch("your-backend-api-url",{ 
      method: "GET",
      credentials: 'include', ⬅️⬅️
      headers: { "Content-Type": "application/json" },
      ... other sptions
    });`
you can learn about fetch here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_credentials_included

TO DO In BACKEND NODE.JS APP

STEP 3: in node.js(or backend app) you need to install cors in app.js( or your main file running server) if you don't know what cors is you may learn from here https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

or you can see video see video: https://www.youtube.com/watch?v=CvKPeWojukc&t=410s

To do this

  1. install cors using --> npm i cors
  2. require cores in app.js -> const cors = require("cors");

STEP 4: use cors in app and pass some arguments as shown in example below along with proxy setting as shown in the code below. And do not forget to add app.set("trust proxy",1); after adding cors options

const app = express(); 
app.use(cors({
    origin: "https://yourfrontendurl.com", ⬅️⬅️
    credentials:  true ⬅️⬅️
}));
app.set("trust proxy",1); ⬅️⬅️
this basically allows your server to accept requests from a specific domain name that is where your front-end website (https://yourfrontendurl.com here in this case) is hosted.

STEP 5: setting up sessions (which I think you have already familiared with) and passing cookie's settings as below

app.use(session({
    secret: process.env.sessionSecret, // your secret key to check session
    resave: false,
    saveUninitialized: false,
    cookie: { maxAge: 604800000, //one week(1000*60*60*24*7)
             sameSite: "none",
             secure : true
            }, 
    store: store
}));

And I also suppose that you are doing other stuffs correctly like setting up mongoDB connections etc. This is also my first answer in stack overflow. Please let me know if somewhere I am doing wrong. I will be happy to know that. You can also see my project page that relating this particular problem solution below.

my package.json file --> https://github.com/satyadalei/mynotepedia-frontend/blob/main/package.json

my app.js file --> https://github.com/satyadalei/mynotepedia/blob/main/app.js

Upvotes: 3

Nicholas
Nicholas

Reputation: 2863

You are correct. Cookies are not cross-domain compatible. If it was, it would be a serious security issue. The easiest way to fix your problem would be to send back the cookie as a res object, and then setting the cookie manually in the frontend.

Take this for example. I'll do this with JavaScript style pseudocode. Don't copy paste this as this most likely wouldn't work right away. This is what you're going to do on the back-end:

// 1. Authenticate the user.
const userData = await authenticateUser();
const userToken = await verifyUser(userData);

// 2. Return the response object.
return response.status(200).json({
  status: 'success',
  data: userData,
  token: userToken,
});

In the front-end:

const response = await axios.post(...); // your API call, will return above object.

// set your authentication token here.
// the 'options' object will contain all possible cookie options, example would be 'secure'.
cookies.set('token', response.data.token, options);

// alternatively, set the cookie in the local storage.
localStorage.setItem('token', response.data.token);

You need to set the cookie accordingly in the front-end.

Further reading: MDN Docs


EDIT: Your question is unclear. First time you talked about cookies, but now you're talking about httpOnly cookies. Please be more specific in your questions.

It is impossible to set httpOnly cookies in React.js if it is cross-domain. React is only responsible for the 'view' of the website. httpOnly cookies are only meant to be set server-side. Client-side JavaScript cannot read or set that specific cookie, but it is able to send it. Unless you have something in your Netlify that can do server-side operations, I don't think that is possible.

Your best bet is to actually just use the same domain.

Upvotes: 7

Related Questions