Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fetch() cannot set cookies received from the server?

I am using Express.js server. With cookie-parser I have opened this endpoint

app.get("/s", (req,res) => {
    res.cookie("bsaSession", req.session.id)
    res.send("set cookie ok")
})

When I manually use the browser to http://localhost:5555/s where I have the website running the browser debug console shows that the cookie have been applied.

enter image description here

But when I use fetch API to do the equivalent, it does not set the cookie.

  async trySetCookie()
  {
    await fetch("http://localhost:5555/s",{
       method: 'GET',
       credentials: 'same-origin'
    })
  }

Why?

like image 918
5argon Avatar asked Mar 10 '17 04:03

5argon


People also ask

How do you send cookies through Fetch?

If you set credentials to same-origin : Fetch will send 1st party cookies to its own server. It will not send cookies to other domains or subdomains. If you set credentials to include : Fetch will continue to send 1st party cookies to its own server.

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.

Are cookies sent with Fetch?

Unless fetch() is called with the credentials option set to include , fetch() : won't send cookies in cross-origin requests. won't set any cookies sent back in cross-origin responses. As of August 2018, the default credentials policy changed to same-origin.

How do you set cookies on a server?

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.


1 Answers

I have found the solution. The core of this problem being that my button to trigger the fetch is on http://localhost:3000/. The server is on http://localhost:5555/ (I am simulating real environment on my own machine)

The problem is that this fetch call

  async trySetCookie()
  {
    await fetch("http://localhost:5555/s",{
       method: 'GET',
       credentials: 'same-origin'
    })
  }

Without credentials, the browser cannot send or receive cookies via fetch (https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)

With credentials as same-origin I can see the cookies coming from the server in the Set-Cookie response header, but nothing is being stored in the browser. One strange thing is that this response always have HttpOnly tagged after the cookie string regardless of my {httpOnly : true/false} settings on the server. In the case of manually using the browser to the page to do GET request, HttpOnly is being respected as usual, and the cookies are set.

So the solution is to set credentials as include to allow cross-origin cookie sending.

  async trySetCookie()
  {
    await fetch("http://localhost:5555/s",{
       method: 'GET',
       credentials: 'include'
    })
  }

Also, on the server side you need to allow a particular origin manually with new headers:

app.get("/s", (req,res) => {
    res.cookie("bsaSession", req.session.id, {httpOnly:false})
    res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
    res.header('Access-Control-Allow-Credentials','true'
    res.send("set")
})

Not doing this results in

XMLHttpRequest cannot load http://localhost:5555/s. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.

But the cookie will be set regardless of this error. Still nice to include that header to silence the error.

If you are using cors middleware for Express it is even easier. You can just use these options

var corsOptions = {
  origin: 'http://localhost:3000',
  credentials:  true
}

app.use(cors(corsOptions))

And of course credentials: 'include' is still required at the client side.

like image 89
5argon Avatar answered Sep 21 '22 12:09

5argon