fetch: Response cookies not being set

I’m trying to implement client login using fetch on react.

I’m using passport for authentication. The reason I’m using fetch and not regular form.submit(), is because I want to be able to recieve error messages from my express server, like: "username or password is wrong".

I know that passport can send back messages using flash messages, but flash requires sessions and I would like to avoid them.

This is my code:

    fetch('/login/local', {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            username: this.state.username,
            password: this.state.password,
          }),
        }).then(res => {
          console.log(res.headers.get('set-cookie')); // undefined
          console.log(document.cookie); // nope
          return res.json();
        }).then(json => {
          if (json.success) {
            this.setState({ error: '' });
            this.context.router.push(json.redirect);
          }
          else {
            this.setState({ error: json.error });
          }
        });

The server sends the cookies just fine, as you can see on chrome’s dev tools: Network - Cookies Network - Headers

But chrome doesn’t set the cookies, in Application -> Cookies -> localhost:8080: “The site has no cookies”.

Using form.submit() while the server sets the cookies and redirects works just fine, the problem only occurs using fetch to retrieve json, so this is why I’m posting it here.

Any idea how to make it work?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 17

Commits related to this issue

Most upvoted comments

Wow. I just realized I did a major mistake.

So, I have two requests; one login request and one customer request. It is the login request that gets the set-cookie header in its response, and then the user should be loggen in.

There is an option called credentials: ‘same-origin’, which I did not send with the login request (because I thought it only needed to be sent with requests after I was logged in.)

Try this:

fetch('/login/local', {
          method: 'POST',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
          },
          credentials: 'same-origin',
          body: JSON.stringify({
            username: this.state.username,
            password: this.state.password,
          }),
        }).then(res => {
          console.log(res.headers.get('set-cookie')); // undefined
          console.log(document.cookie); // nope
          return res.json();
        }).then(json => {
          if (json.success) {
            this.setState({ error: '' });
            this.context.router.push(json.redirect);
          }
          else {
            this.setState({ error: json.error });
          }
        });

Make sure you don’t add the credentials: ‘same-origin’ in the header object. It is not supposed to be there. I’ve seen many do that mistake.

I had to use credentials: 'include' on the client side and CORS_ALLOW_CREDENTIALS = True in my django app. Also setting my cookie with 127.0.0.1 in localhost response.set_cookie('my_cookie', value=token, httponly=True, domain='127.0.0.1')

My two cents: If on DEV don’t use localhost as the domain value of the Cookie, use 127.0.0.1 instead.

Resources

I was too under the impression that credentials: same-origin is used only for sending cookies, not receiving. Thank you, I’ll try this as soon as I can.

If that is indeed the solution, it might be a good idea to add this to the documentation/README.

@ramprasadgs Is the signup request the one that is supposed to set the cookies in your browser or is it a request happening before/after this request that are missing the credentials option?

It’s been a while since I worked with this, and I don’t remember exactly when to use the option. It is either one of these two cases (or both):

A) Use the option when sending the request that logs a user in aka. receives the session cookie for the server.

B) Use the option when sending all requests after the login request is sent.

I think it’s best to just use the option in all requests when debugging, and when it works, see where you can remove it. The docs are still confusing, so it’s not possible to understand if the option must be used when logging in OR when sending requests as a logged in user.

Btw, are you getting a response from the sign up request? You should be able to get a response from a login-request, but if I remember correctly, the problems starts when you’re doing the next request and the cookie is not set.

EDIT: Btw, make sure the problem is not caused by CORS issues. As your backend and fronend are on different domains (the ports are not the same), you may have to use the credentials: include option. But I don’t know how this option works, so I am not sure. https://github.com/github/fetch#sending-cookies

I don’t have any samples, its my day job 😃 for basic and even some useful apps, react experience is enough. Anything really specific might need some actual android or ios knowledge.

So, I’ve verified that my issue is not caused by the server (like not setting path or somewthing).

I opened chrome://net-internals/#events and looked at the difference between a fetch call and $.ajax call (which sets the cookie correctly).

There is one thing in particular i noticed:

$.ajax:


t=587184 [st= 2]   +URL_REQUEST_START_JOB  [dt=6]
                    --> load_flags = 33026 (BYPASS_CACHE | MAYBE_USER_GESTURE | VERIFY_EV_CERT)
                    --> method = "POST"
                    --> priority = "MEDIUM"
                    --> upload_id = "0"
                    --> url = "http://localhost:8080/api/v1/login"

fetch:

t=622664 [st= 2]   +URL_REQUEST_START_JOB  [dt=8]
                    --> load_flags = 34626 (BYPASS_CACHE | DO_NOT_SAVE_COOKIES | DO_NOT_SEND_AUTH_DATA | DO_NOT_SEND_COOKIES | MAYBE_USER_GESTURE | VERIFY_EV_CERT)
                    --> method = "POST"
                    --> priority = "MEDIUM"
                    --> upload_id = "0"
                    --> url = "http://localhost:8080/api/v1/login"

See the load flags? Not sure what those are, but I guess that is what causing the problem.