Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cookie in Set-Cookie header not being set

I'm sending a request to a node.js server from a reactjs client using axios as shown below.

import axios from 'axios';

const apiClient = axios.create({
  withCredentials: true,
  baseURL: 'http://localhost:8080'
});

async function(payload) {
  try {
    debugger;
    let result = await apiClient.post('/auth/signup/', payload);
    debugger;
    return result;
  } catch (error) {
    debugger;
    throw error;
  }
}

The node.js endpoint sets a cookie in the response as shown below.

const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')
const cors = require('cors');
const jwt = require('jsonwebtoken');

router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
router.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag']} ));
router.use(cookieParser());


router.post('/signup', async (req, res, next) => {
  debugger;
  let database = req.app.locals.database;
  try {
    let user = await database.findByUsername(req.body.username);
    let token = await jwt.sign({username: user.username}, config.secret, {expiresIn: "15m"});
    res.cookie('jwt',  token, {
      maxAge: 900,
    });
  } catch (error) {
    debugger;
    return res.status(503).send({ auth: false, message: 'Database error.' });
  }
});

The Set-Cookie header of the response contains the cookie as expected.

Set-Cookie Header

However, Chrome does not appear to be setting the cookie, as I cannot see the cookie in the Application window of the Developer Console.

Cookies

I've looked at the answers to the following questions, which mention setting { withCredentials: true } in the axios configuration and not using a wildcard origin for cors in node.js, but I am already doing both.

Set-Cookie header not setting cookie in Chrome

Set cookies for cross origin requests

Any ideas as to why the cookie is not being set and how to fix this issue?

like image 476
V. Nitu Avatar asked May 02 '20 05:05

V. Nitu


People also ask

Why is cookie not being set?

Check out the OPTIONS response header ACCESS-CONTROL-ALLOW-CREDENTIAL whether it is set to true . If the server doesn't allow credentials being sent along, the browser will just not attach cookies and authorization headers. So this could be another reason why the cookies are missing in the POST cross-site request.

How do you set a cookie header?

The Set-Cookie HTTP response header is used to send a cookie from the server to the user agent, so that the user agent can send it back to the server later. To send multiple cookies, multiple Set-Cookie headers should be sent in the same response.

What is the difference between set-cookie and cookie header?

The Set-Cookie header is sent by the server in response to an HTTP request, which is used to create a cookie on the user's system. The Cookie header is included by the client application with an HTTP request sent to a server, if there is a cookie that has a matching domain and path.

Is cookie stored in header?

The Cookie HTTP request header contains stored HTTP cookies associated with the server (i.e. previously sent by the server with the Set-Cookie header or set in JavaScript using Document. cookie ). The Cookie header is optional and may be omitted if, for example, the browser's privacy settings block cookies.


2 Answers

Though you are hosting client and server in the same domain as http://localhost, your ports are different, so the same-origin policy is failed here. You can check https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy.

As so, you making a CORS request, check your network tab in your developer tools in your current browser, you might see a preflight request OPTIONS, before your client sends POST request to your server.

The server must specify headers to accept the origin of your next request - POST request from http://localhost:8000 with method POST, you can refer to https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request

HTTP/1.1 204 No Content
Connection: keep-alive
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST // Your next request will use POST method
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true // cookies accepted

Added:

In Set-Cookie, Max-Age must be non-zero digit. It be rounded up into integer according to RFC doc. For express.js, cookies `maxAge property is on the scale of miliseconds

The solution will be set the maxAge property as second * 1000

    res.cookie('jwt',  token, {
      maxAge: 10000,
    });
like image 91
trmaphi Avatar answered Oct 22 '22 05:10

trmaphi


Here's a repost of my answer on a similar question https://stackoverflow.com/a/62821342/8479303


In my case, the network panel showed that the response had the 'Set-Cookie' header, but in axios the header wouldn't show up, and the cookie was being set.

For me, the resolution was setting the Access-Control-Expose-Headers header.

For explanation, from this comment on an issue in the axios repository I was directed to this person's notes which led me to set the Access-Control-Expose-Headers header -- and now the cookie is properly setting in the client.

So, in Express.js, I had to add the exposedHeaders option to my cors middleware:

const corsOptions = {
  //To allow requests from client
  origin: [
    "http://localhost:3001",
    "http://127.0.0.1",
    "http://104.142.122.231",
  ],
  credentials: true,
  exposedHeaders: ["set-cookie"],
};

...

app.use("/", cors(corsOptions), router);

It was also important that on the axios side I use the withCredentials config in following axios requests that I wanted to include the cookies.

ex/

const { data } = await api.get("/workouts", { withCredentials: true });
like image 41
jnotelddim Avatar answered Oct 22 '22 06:10

jnotelddim