5

I have a response from my server. Response Headers contains a valid Set-Cookie. Chrome Response/Cookies says that I have 1 cookie, ok. But... The cookie is not setting into DevTools/Application/Cookies

/*
 My frontend on Vue
 I renamed 127.0.0.1 to www.example.com
 So running on www.example.com:80 or just www.example.com
*/
let response = await axios({
               method: 'post',
               url: 'http://127.0.0.1:3000/api/login', // my Node server on 3000 port   
               data: {
                   email : login,
                   password: password,
               },
            })
/*
 My backend part on Node.js+Express
 Running on localhost:3000 or 127.0.0.1:3000
 CookieParser() is setted
*/
let options = {
              maxAge: 345600000,
              httpOnly: true,
              path: '/',
            }

res
   .cookie('test', 'test', options)
   .cookie('access_token', token, options) //token contains a JWT string
   .send('');
/*
 Chrome Response Headers
*/
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 09 Feb 2020 08:52:22 GMT
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
Set-Cookie: test=test; Max-Age=345600; Path=/; Expires=Thu, 13 Feb 2020 08:52:22 GMT; HttpOnly
Set-Cookie: access_token=example; Max-Age=345600; Path=/; Expires=Thu, 13 Feb 2020 08:52:22 GMT; HttpOnly
X-Powered-By: Express
/*
 Chrome request headers
*/
Accept: application/json, text/plain, '*/*'
Accept-Encoding: gzip, deflate, br
Accept-Language: ru-RU,ru;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 45
Content-Type: application/json;charset=UTF-8
Host: 127.0.0.1:3000
Origin: http://www.example.com
Pragma: no-cache
Referer: http://www.example.com/
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36

Moreover I tried to use Microsoft Edge, but the problem isn't gone. Why browsers can't set-cookie considering they know about receiving Cookies?

professrr
  • 75
  • 1
  • 5
  • 2
    Is the request same-origin or cross-origin? Are you using http or https? Do you have any other directives set on the cookie, e.g. `Expires`, `Max-Age`, `Domain`, etc.? – skirtle Feb 08 '20 at 23:28
  • There is not enough detail here for us to help. Please read [How do I ask a good question](https://stackoverflow.com/help/how-to-ask) which is really "How do I ask a question that people can answer quickly and accurately?". Also, see [How to create a minimal, reproducible example?](https://stackoverflow.com/help/minimal-reproducible-example). – jfriend00 Feb 09 '20 at 03:51
  • If you go to the Chrome debugger and go to the network tab, and show us exactly what is both the raw request and the response look like there (particularly so we can see the domains and see the exact cookie that the server sent back, then we could perhaps have some ideas what's going on. – jfriend00 Feb 09 '20 at 03:53
  • @skirtle Sorry guys! Yesterday I was a little bit mad and tired of solving this problem. I have already rewritten my post and added extra info – professrr Feb 09 '20 at 09:06
  • @jfriend00 Sorry guys! Yesterday I was a little bit mad and tired of solving this problem. I have already rewritten my post and added extra info – professrr Feb 09 '20 at 09:06
  • So, this is apparently a cross origin request. In order to send cookies with a cross origin request, you need the `withCredentials: true` options for `axios()` or `credentials: 'include'` for `fetch()`. – jfriend00 Feb 09 '20 at 09:46

2 Answers2

10

If you want to do some background reading then I suggest you look for guides to CORS (cross-origin resource sharing) and, specifically, how to set cookies when using CORS.

Using your original code you'll need to change it to set withCredentials to true:

let response = await axios({
    method: 'post',
    url: 'http://127.0.0.1:3000/api/login', // my Node server on 3000 port   
    data: {
        email : login,
        password: password,
    },
    withCredentials: true
})

Behind the scenes this will set the withCredentials flag on XMLHttpRequest:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials

You'll then get CORS errors in your console. If you work your way through those errors you should be able to get it working. These errors will include...

Firstly, the server will need to return the header Access-Control-Allow-Credentials: true. This also applies to the preflight OPTIONS request.

Secondly, you're currently using Access-Control-Allow-Origin: * but that isn't allowed for requests using credentials. That'll need to be Access-Control-Allow-Origin: http://www.example.com. Likewise for the preflight.

At that point the cookie should be set in most browsers (more on browser quirks later).

To get the cookies added to subsequent requests you'll also need to set withCredentials: true on those requests. The other header changes outlined above also need to be applied. The cookies will not be included on the preflight request, just the main request.

Seeing the cookies in the browser's developer tools is not completely straightforward. Generally the tools only show cookies set for the same origin as the current page, which doesn't include the cookies set via CORS requests. To see those cookies you'll need to open another browser tab and set the URL to something that starts with the same origin as the CORS request. Be careful though, you need to pick a URL that won't set the cookies itself, otherwise that doesn't really prove anything.

Another way to check what cookies are set is to send another request (with withCredentials) and see what cookies get sent.

Then we have the browser-specific issues...

In Safari it is really difficult to get cookies to set over CORS. If Safari is a target browser for you then I suggest doing some further research into that.

Secondly, Chrome 80 is due to change the rules around CORS cookies significantly. See:

https://www.chromium.org/updates/same-site

You'll already see warnings about this in your console with older versions of Chrome. In short, CORS cookies will need to be served over https and set the directives Secure and SameSite=None.

Update:

Since writing this answer I have created an FAQ for common CORS problems. The section explaining the use of cookies can be found at https://cors-errors.info/faq#cdc8.

skirtle
  • 22,128
  • 2
  • 23
  • 49
  • It’s the best answer ever! But is there any reasons to use CORS? Or i won't get away from the problem, of it is a standard? Or CORS is best practice?... – professrr Feb 11 '20 at 06:00
  • @professrr The only way to avoid CORS is to make all requests to the same origin. Depending on what you're trying to achieve that may well be an option. Multiple servers can be combined into a single server using reverse proxying. For example, if you're using the Vue CLI you can read more about configuring proxying during development at https://cli.vuejs.org/config/#devserver-proxy. – skirtle Feb 11 '20 at 06:15
  • That's a very nice question. It seems to me that it's gonna solve my issue. thanks for sharing – Kimsea Sok Nov 17 '20 at 18:26
1

if you use Axios or Fetch - they don't send cookies by default. if you go and check in the dav tools - (application tab -> cookies) you should see them. it's not chromes problem but the way you trying to send them back, in fetch you need to add the credentials: 'include', to the option object, in axios it's {withCredentials: true}

Yosi Leibman
  • 220
  • 2
  • 11