16

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?

1
  • 3
    Good question. Inserting images into it, upvoting. Commented May 2, 2020 at 6:51

4 Answers 4

4

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, }); 
Sign up to request clarification or add additional context in comments.

4 Comments

Just check the cors middleware of express. It seems to do all the work for setting CORS config in the pre-flight response. Now, I'm very interested in what the response will look like.
Preflight requests can no longer be viewed in Chrome DevTools according to this source: httptoolkit.tech/blog/chrome-79-doesnt-show-cors-preflight. I am also not quite sure what exactly you are saying I should be looking for in the preflight request / response. Could you please clarify?
From the screen, it looks like you have no problem with the values of HTTP cookies being responded by the server. Then, check the Max-Age in your Set-Cookie, it might be the problem, as IIRC that value must be non-zero digit.
Thank you, a Max-Age value of 0 was indeed the issue. I thought I was setting the Max-Age with res.cookie('jwt', token, {maxAge: 900}); in node, but looking at the Set-Cookie header, the Max-Age was 0. Although the maxAge property in the cookie options in node is in milliseconds, the Max-Age in the Set-Cookie header is in seconds, so my value of 900 milliseconds was being rounded down to 0 seconds. Add the information in your comment to your answer and I will accept it.
4

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 }); 

1 Comment

exposedHeaders: ["set-cookie"] seems to be critical when using Axios on the front end
1

If anyone having the same problem, this might help.

Frontend: If using axios then add: withCredentials: true. For fetch() add: credentials: 'include'.

Backend: If you're on http://localhost:.... then you could add to your cookie sameSite:'lax', just make sure you're not adding Secure to this cookie while you on http://localhost because it is only for https not http, else your cookie won't be added to the browser. While in production adding sameSite: 'none' and Secure is ok.

I've added some headers too:

res.setHeader('Access-Control-Allow-Credentials', true); res.setHeader('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type, Accept, authorization') res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin','http://localhost:3000'); res.setHeader('Access-Control-Allow-METHODS',"GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH"); 

Also added:

app.use(cors({ origin: 'http://localhost:3000', credentials: true })) 

Just make sure you're not using '*' when adding your origin in headers or cors. Write as shown in an example above : origin: 'http://localhost:3000' and res.setHeader('Access-Control-Allow-Origin','http://localhost:3000');

All of the above helped me with my project, I hope it will help someone else too.

Comments

0

Alright, to all who are facing exactly same issue that they are getting cookie in network but not able to set it in frontend (Assuming both are running locally)

For you express server the code should look like this

const app = express() app.use(bodyParser.json()) app.use(cookieParser()) app.use(cors({ origin: "http://localhost:5173", credentials: true, // exposedHeaders: ["set-cookie"] })) 

and your response return should look like this it should include maxAge

res.status(200).cookie("authToken", jwtToken, { maxAge: 10000, // httpOnly: false, //this should be false when working locally }).json({ email, given_name, family_name, jwtToken }); 

After this the only thing required in front end is add withCredentials : true where you are making api call to set cookies

axios.defaults.withCredentials = true; const response = await axios.post(Url, { clientId, token }); 

This should work fine

1 Comment

If you cookie is going away after refreshing the page please uncomment exposedHeaders: ["set-cookies] in above code

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.