Fetch API with Cookie
Asked Answered
F

11

352

I am trying out the new Fetch API but is having trouble with Cookies. Specifically, after a successful login, there is a Cookie header in future requests, but Fetch seems to ignore that headers, and all my requests made with Fetch is unauthorized.

Is it because Fetch is still not ready or Fetch does not work with Cookies?

I build my app with Webpack. I also use Fetch in React Native, which does not have the same issue.

Fancher answered 1/1, 2016 at 17:21 Comment(0)
F
393

Fetch does not use cookie by default. To enable cookie, do this:

fetch(url, {
  credentials: "same-origin"
}).then(...).catch(...);
Fancher answered 4/1, 2016 at 13:34 Comment(13)
same-origin doesn't work anymore, include does (see @Jerry's answer): developers.google.com/web/updates/2015/03/introduction-to-fetchCommunalism
@jpic: 'include' only works for cross-origin requests, but not for same-origin requests. Official docs: github.com/github/fetch#sending-cookiesHebdomadary
What is the reason then to have httponly cookies if they are readable in js with fetch ?Reo
I believe same-origin (which does still work) means that more headers will be respected (cookies, etc) but your code will have limited access to the response.Crinoline
Wasted a lot of time trying to figure out the problem in the server side. Thanks a lot!Kneepan
@MartinBajcar parameter "credentials" in fetch is not reading any cookie, it only acts as a door, if opened(credentials: 'include'), cookies will be allow to pass, if not opened then they aren't.Dreary
@JohnBalvinAriasThx. As I later understood, having cookie httponly means that it's not readable by document.cookie, but still available to ajax or fetch requests.Reo
i am unable to get "set-cookie" parameter in response headers in fetch api in android. in ios i am getting it for first time. how to get "set-cookie" every time i login in both android and ios. i used to achieve this in native ios by setting setHTTPShouldHandleCookies to NO. thanks in advancePollen
@DhanunjayKumar If you have a new question, please use the Ask Question button. Comments are not a good avenue for getting questions answered.Boltzmann
@HereticMonkey : i have added as question also link : #50905245Pollen
Hello guys, I was reading this question, and I have a similar one, which my cookies are set to HttpOnly and from javascript when I fetch an API it redirects to login url because it cannot reach the cookies, where credentials:same-origin didnt work for me, can you please check my question? #59311703Omphalos
what if we have different origins.Louvre
same origin is the default value now. assuming this is cors, which was my issue when I came here, @zurfyx answer should probably be the accepted one.Susan
B
318

In addition to @Khanetor's answer, for those who are working with cross-origin requests: credentials: 'include'

Sample JSON fetch request:

fetch(url, {
  method: 'GET',
  credentials: 'include'
})
  .then((response) => response.json())
  .then((json) => {
    console.log('Gotcha');
  }).catch((err) => {
    console.log(err);
});

https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials

Blench answered 13/8, 2016 at 18:38 Comment(11)
how do you set the cookie though?Camden
The cookie is set from server-side. In my case I was using httponly cookies.Fancher
@Fancher can I set cookies using the document.cookie by javascript, and then send the request?Teocalli
@Teocalli You can send it in the header.Devisee
Interesting fact: I wanted to cover both cases of fetch with same-origin and cross-origin by using this, and I could not, but it did work for cross-origin requests. =)Hebdomadary
@Teocalli I found that just setting the value in document.cookie was enough for it to be included in the requests.Baroscope
i am unable to get "set-cookie" parameter in response headers in fetch api in android. in ios i am getting it for first time. how to get "set-cookie" every time i login in both android and ios. i used to achieve this in native ios by setting setHTTPShouldHandleCookies to NO. thanks in advancePollen
If you're using a rails API and keep getting CORS issues with Access-Control-Allow-Credentials make sure in your Rack::Cors config you set credentials: true on the resource.Pneumonectomy
@Camden dough* - refrigerate it for 24hrs, it should be fully set by thenNolita
See this solutionOctopus
This made an end to my countless hour journey on why setting my cookie doesn't work. Thank you so very much.Rutherford
H
124

Have just solved. Just two f. days of brutforce

For me the secret was in following:

  1. I called POST /api/auth and see that cookies were successfully received.

  2. Then calling GET /api/users/ with credentials: 'include' and got 401 unauth, because of no cookies were sent with the request.

The KEY is to set credentials: 'include' for the first /api/auth call too.

Hypothesize answered 7/8, 2018 at 11:47 Comment(10)
I have exactly your problem. The session cookie is never sent on the GET data request. so 401. I have tried Axios and Fetch. same result. 2 possibilities: the login POST doesnt store the received cookie or the following GET data doesnt send the stored cookieSheepcote
@Rhubarb65, to win this u should specify credentials: 'include' for first POST /api/authHypothesize
Yes, I had that but it want enough. I use a devserver proxy (client Http)Sheepcote
Yes, I had credentials but it was not enough. I was using a devserver proxy to get past CORS: (client http) - proxy - (server https). I believe this meant that the sessionid cookie from the server was not set in the browser because secure cookies require https. So I added the flag https: true in the devserver proxy and that fixed itSheepcote
Well cheers. Your answer meant it only took me 1 day of bruteforce. :)Phippen
As I remember, used browser dev tools and backend logs (request data) to understand that no cookies were sent. After I randomly (i think so, may be from google search) added needed param to first auth call. PS hehe - found that in profile my location is moon (:Hypothesize
I owe you a big fat pint of beer. I've been banging my head against the desk for the past 3 hours trying to solve this issue. Thank you x1000.Faulkner
Also, don't be me. Make sure the fetch URL is 127.0.0.1 not localhost, or the Cookie won't be set.Angers
That's amazing! I would never have thought to try that. What's the rationale to also need to try and send a cookie on the first request?Eluvium
You are a legend. 2 days of bruteforce here as well until I read your answer.Tridentine
H
35

If you are reading this in 2019, credentials: "same-origin" is the default value.

fetch(url).then
Heterolysis answered 13/6, 2019 at 8:39 Comment(1)
But note that not everyone is using a sufficiently updated browser. I came across this question because my own version of Firefox (60.x, the most recent in Debian Stretch) doesn't set that by default.Sardonic
E
24

Programmatically overwriting Cookie header in browser side won't work.

In fetch documentation, Note that some names are forbidden. is mentioned. And Cookie happens to be one of the forbidden header names, which cannot be modified programmatically. Take the following code for example:

  • Executed in the Chrome DevTools console of page https://httpbin.org/, Cookie: 'xxx=yyy' will be ignored, and the browser will always send the value of document.cookie as the cookie if there is one.
  • If executed on a different origin, no cookie is sent.
fetch('https://httpbin.org/cookies', {
  headers: {
    Cookie: 'xxx=yyy'
  }
}).then(response => response.json())
.then(data => console.log(JSON.stringify(data, null, 2)));

P.S. You can create a sample cookie foo=bar by opening https://httpbin.org/cookies/set/foo/bar in the chrome browser.

See Forbidden header name for details.

Evasive answered 1/9, 2021 at 14:22 Comment(1)
It works for me: headers: { Cookie: cookies().toString() },Helping
R
4

Just adding to the correct answers here for .net webapi2 users.

If you are using cors because your client site is served from a different address as your webapi then you need to also include SupportsCredentials=true on the server side configuration.

        // Access-Control-Allow-Origin
        // https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
        var cors = new EnableCorsAttribute(Settings.CORSSites,"*", "*");
        cors.SupportsCredentials = true;
        config.EnableCors(cors);
Rigney answered 27/8, 2019 at 16:57 Comment(3)
'cors.SupportsCredentials = true;' saved my day, thanks.Wadewadell
Completely unrelated to the question. This is about browser APIs and React Native, not some other platform.Phineas
@OlegMihailik this was just to point out that not setting up CORS correctly will cause a similar error. My answer is not releated to React Native; though, I suspect, it could manifest there as well.Rigney
C
2

This works for me:

import Cookies from 'universal-cookie';
const cookies = new Cookies();

function headers(set_cookie=false) {
  let headers = {
    'Accept':       'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
};
if (set_cookie) {
    headers['Authorization'] = "Bearer " + cookies.get('remember_user_token');
}
return headers;
}

Then build your call:

export function fetchTests(user_id) {
  return function (dispatch) {
   let data = {
    method:      'POST',
    credentials: 'same-origin',
    mode:        'same-origin',
    body:        JSON.stringify({
                     user_id: user_id
                }),
    headers:     headers(true)
   };
   return fetch('/api/v1/tests/listing/', data)
      .then(response => response.json())
      .then(json => dispatch(receiveTests(json)));
    };
  }
Celestina answered 6/5, 2020 at 21:54 Comment(1)
Thanks! This is exactly what I was trying to figure out!! 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') saved the day!Ratcliff
M
2

If it still doesn't work for you after fixing the credentials.

I also was using the :

  credentials: "same-origin"

and it used to work, then it didn't anymore suddenly, after digging much I realized that I had change my website url to http://192.168.1.100 to test it in LAN, and that was the url which was being used to send the request, even though I was on http://localhost:3000.

So in conclusion, be sure that the domain of the page matches the domain of the fetch url.

Morrissette answered 27/1, 2022 at 8:49 Comment(4)
Any ideas if it's port specific? Curious how this works when the host is the same but the port is different? (ie. UI server + backend server)Jervis
Hello @Jervis Yes actually it may seem that ports aren't related to cors but they are : https://mcmap.net/q/76922/-cors-error-on-same-domainMorrissette
note, that also localhost is not the same as 127.0.0.1 - your backend needs to be on the exact same ip address than your frontendFribourg
or.. just use include instead of same-origin.Susan
A
1

My issue was my cookie was set on a specific URL path (e.g., /auth), but I was fetching to a different path. I needed to set my cookie's path to /.

Alcock answered 2/7, 2021 at 19:46 Comment(0)
N
1

I wasted three hours looking for a solution so I decided to share my findings.

login(email: string, password: string): Promise<User | null> {

  return fetch('http://localhost:8080/api/login', {
    method: 'POST',
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: email,
      password: password,
    }),
  })
      .then(response => {
        this.handleError(response);
        return response.ok ? response.json() : null;
      })
}
  1. "credentials: 'include'" parameter must be set in the request that receives Set-Cookies header, not just subsequent request that we expect to carry the cookie;
  2. Most most importantly Access-Control-Allow-Headers returned by the server MUST list all possible headers that client MAY send. Contrary to your expectation wildcard asterisk value won't work. If client sends at least one unknown header it will also break cors.
Northwestward answered 27/3, 2024 at 9:32 Comment(0)
N
0

If the fetch is not sending credentials even though the request is not intended to be a cross-origin call, it could be because the request is being processed as cross-origin due to differences in protocols between the origin of the request and location of the response.

I observed that my server was returning Location header with an http URL, while the connection was established over https. As a result, the browser treated the request as cross-origin and applied cross-origin rules. It's worth noting that Firefox (version 114.0.2) and Safari (version 16.1) didn't display any warnings in this scenario, but Chrome (version 114.0.5735.198) showed a (blocked:mixed-content) error, which helped in identifying the issue.

If anyone is interested, in this particular case, SSL termination was being performed in the reverse-proxy, but the gunicorn server was not correctly handling it due to misconfiguration, specifically related to the secure-scheme-headers and forwarded_allow_ips settings. After resolving these settings on the server side, fetch started working fine in all browsers.

Newish answered 2/7, 2023 at 20:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.