25

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".

Any idea how to make it work?

Gershon Papi
  • 5,008
  • 3
  • 24
  • 50

4 Answers4

15

The problem turned out to be with the fetch option credentials: same-origin/include not being set. As the fetch documentation mentions this option to be required for sending cookies on the request, it failed to mention this when reading a cookie.

So I just changed my code to be like 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 => {
      return res.json();
    }).then(json => {
      if (json.success) {
        this.setState({ error: '' });
        this.context.router.push(json.redirect);
      }
      else {
        this.setState({ error: json.error });
      }
    });
Gershon Papi
  • 5,008
  • 3
  • 24
  • 50
  • 3
    This didn't work for me , even after setting the credentials: 'same-origin',/'include. – svp Feb 26 '18 at 13:02
  • This does not work even with the credentials as same-origin – Satyam S Apr 04 '19 at 13:19
  • 1
    credentials: 'same-origin' is the default value, no? https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials – ulu Apr 12 '19 at 18:02
  • Not working credentials: 'same-origin' or 'include' – A.W. Dec 27 '19 at 14:10
  • 3
    I fixed this by setting credentials: 'include' and also setting the header "Access-Control-Allow-Origin" to "http://localhost:3000" (my Webstorm is serving the react pages from a different server than my express server) and also setting the header "Access-Control-Allow-Credentials" to "true" – Mark May 05 '20 at 10:28
  • I did as Mark above, but it didn't work for me. But it turns out you can just access `document.cookie`; here's how I accessed a JWT inside a bearer token set-cookie Authorization response: ```const cookie = document.cookie;const findAuthorization = 'Authorization=';const startIndex = cookie.indexOf(findAuthorization) + findAuthorization.length;const endIndex = cookie.indexOf(';', startIndex) ?? cookie.length - 1;const rawJwt = cookie.slice(startIndex, endIndex).split(' ')[1]; ``` – koral Jul 20 '20 at 15:33
  • 5
    This answer doesn't actually show how to get a cookie value from the response, which was the original question. – Brian White Feb 18 '23 at 01:58
8

From Differences from jQuery section of the Fetch API on Mozilla:

  • fetch() won't receive cross-site cookies. You can’t establish a cross site session using fetch(). Set-Cookie headers from other sites are silently ignored.
  • fetch() won’t send cookies, unless you set the credentials init option. Since Aug 25, 2017: The spec changed the default credentials policy to same-origin. Firefox changed since 61.0b13.)
Konstantin Komelin
  • 666
  • 1
  • 8
  • 12
0

I spent a long time but nothing worked for me.

after trying several solutions online this one worked for me.

Hopefully it will work for you too.

{
  method: "POST",
  headers: {
    "content-type": "API-Key",
  },
  credentials: "include",
}
Mohsin Latif
  • 477
  • 6
  • 7
0

I had to include credentials: 'include' in the fetch options:

fetch('...', {
  ...
  credentials: 'include', // Need to add this header.
});
Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141