Reputation: 12302
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.
Upvotes: 367
Views: 544307
Reputation: 4089
On modern browsers, fetch()
now sends cookies by default if the origin of the endpoint and the origin of the calling script are the same.
The value of credentials
is no longer "omit"
but now "same-origin"
.
To send to an endpoint on another origin, set credentials
to "include"
:
fetch(url, { credentials: "include" })
Upvotes: 3
Reputation: 30165
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;
})
}
Set-Cookies
header, not just subsequent request that we expect to carry the cookie;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.Upvotes: 6
Reputation: 373
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.
Upvotes: 0
Reputation: 3168
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.
Upvotes: 2
Reputation: 3402
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:
Cookie: 'xxx=yyy'
will be ignored, and the browser will always send the value of document.cookie
as the cookie if there is one.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.
Upvotes: 25
Reputation: 6642
My issue was my cookie was set on a specific URL path (e.g., /auth
), but I was fetch
ing to a different path. I needed to set my cookie's path
to /
.
Upvotes: 1
Reputation: 12302
Fetch does not use cookie by default. To enable cookie, do this:
fetch(url, {
credentials: "same-origin"
}).then(...).catch(...);
Upvotes: 404
Reputation: 1649
Have just solved. Just two f. days of brutforce
For me the secret was in following:
I called POST /api/auth and see that cookies were successfully received.
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.
Upvotes: 128
Reputation: 2364
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)));
};
}
Upvotes: 2
Reputation: 416
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);
Upvotes: 4
Reputation: 4300
If you are reading this in 2019, credentials: "same-origin"
is the default value.
fetch(url).then
Upvotes: 36
Reputation: 32807
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
Upvotes: 329