Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incompatibility with Fetch API #13

Closed
sshymko opened this issue Mar 10, 2017 · 15 comments
Closed

Incompatibility with Fetch API #13

sshymko opened this issue Mar 10, 2017 · 15 comments

Comments

@sshymko
Copy link

sshymko commented Mar 10, 2017

The cookie parser does not recognize response returned by the Fetch API. That makes it nearly impossible to use the library in browser/Node.js/React Native environments.

The library assumes response to be a plain object with the set-cookie property. However, the Fetch Response has a different interface:

response.headers.get('Set-Cookie')
response.headers.getAll('Set-Cookie')
@nfriedly
Copy link
Owner

nfriedly commented Mar 10, 2017

That's a good point, this was written before fetch was a thing, so "response object" implied "node.js response object". I'll update the readme to make that more clear.

However, it also accepts a single Set-Cookie header value, an array of Set-Cookie header values. I haven't tried it yet, but I'm pretty sure that if you pass in the output from response.headers.get('Set-Cookie') or response.headers.getAll('Set-Cookie'), it will parse either of them correctly. Can you confirm?

@nfriedly
Copy link
Owner

nfriedly commented Mar 10, 2017

If you'd like to send a PR to make it recognize fetch-style responses, that'd be welcome also ;)

nfriedly added a commit that referenced this issue Mar 10, 2017
@Ashoat
Copy link

Ashoat commented Mar 13, 2017

It doesn't seem like passing the output from the fetch API works in all cases. It seems like "httponly" without a subsequent "=" will break the multi-cookie-parsing logic. Here's an example of what I'm seeing:

console.log(response.headers.get('Set-Cookie'));
> "anonymous=39070%3A0d96462004e99ee7c13af7d8ebd1bdffc9abf6b2c95070429a1737fe37f43154; expires=Wed, 12-Apr-2017 19:58:28 GMT; Max-Age=2591999; path=/~ashoat/squadcal/; domain=localhost; httponly, user=39071%3A9d3f1baa12032ce4a2120c0c72e75297f3620defaad8c09905c4d03a92c87aad; expires=Wed, 12-Apr-2017 19:58:29 GMT; Max-Age=2592000; path=/~ashoat/squadcal/; domain=localhost; httponly, anonymous=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/~ashoat/squadcal/; domain=localhost; httponly"
console.log(response.headers.getAll('Set-Cookie'));
// note that whole cookie string (including multiple cookies) is returned as one header by getAll
> ["anonymous=39070%3A0d96462004e99ee7c13af7d8ebd1bdffc9abf6b2c95070429a1737fe37f43154; expires=Wed, 12-Apr-2017 19:58:28 GMT; Max-Age=2591999; path=/~ashoat/squadcal/; domain=localhost; httponly, user=39071%3A9d3f1baa12032ce4a2120c0c72e75297f3620defaad8c09905c4d03a92c87aad; expires=Wed, 12-Apr-2017 19:58:29 GMT; Max-Age=2592000; path=/~ashoat/squadcal/; domain=localhost; httponly, anonymous=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/~ashoat/squadcal/; domain=localhost; httponly"]
// same result occurs with headers.getAll
console.log(setCookie.parse(response.headers.get('Set-Cookie')));
> [ { domain: "localhost", expires: "Wed Dec 31 1969 19:00:01 GMT-0500 (EST)", httpOnly: true,
"httponly, anonymous": "deleted", "httponly, user": "39071%3A9d3f1baa12032ce4a2120c0c72e75297f3620defaad8c09905c4d03a92c87aad", maxAge: 0, name: "anonymous", path: "/~ashoat/squadcal/", value: "39070:0d96462004e99ee7c13af7d8ebd1bdffc9abf6b2c95070429a1737fe37f43154" } ]
// note that only one cookie is returned, with keys like "httponly, user"

@nfriedly
Copy link
Owner

nfriedly commented Mar 13, 2017

Oh, geze. I didn't realize that .get() and .getAll() join all of the headers into a single comma-separated string. That's going to be tricky to solve in the library because a header value could legitimately contain a comma. If you're pretty sure that the headers you're going to be working with won't contain that string, then you can do something like this:

setCookie.parse(response.headers.get('Set-Cookie').split(/, /g));

@nfriedly
Copy link
Owner

nfriedly commented Mar 13, 2017

I think I found a better solution - using a for/of loop, it's possible to access each header individually:

var cookies = [];
for(var header of response.headers) {
  if (header[0] === 'set-cookie') {
    cookies.push(setCookie.parse(header[1]);
  }
}
console.log(cookies);

Also, how are you getting access to the Set-Cookie headers? The browser seems to block me from seeing them. Nevermind, you said "browser/Node.js/React Native" - this should work for Node and maybe React Native but I suspect nothing is going to work for browsers.

@Ashoat
Copy link

Ashoat commented Mar 13, 2017

Oh, geze. I didn't realize that .get() and .getAll() join all of the headers into a single comma-separated string.

It looks like according to RFC 6265, only a single Set-Cookie header can be set, and the cookies are thus joined into a single header by the server. I suspect that Node.js is handling the logic of splitting the single header into an array.

EDIT oops I may be wrong here - that's referring to the Cookie header, not the Set-Cookie header. It may be the fetch API that is doing the joining yet. Note that I'm using React Native, which has a custom (probably isomorphic?) implementation of fetch. My server is a simple PHP script that does multiple set_cookie calls.

setCookie.parse(response.headers.get('Set-Cookie').split(/, /g));

This won't work because the "expires" clause can contain a comma, as it does in the example I gave above.

using a for/of loop, it's possible to access each header individually:

I don't follow the logic here - if there's only a single Set-Cookie header, won't this just lead to setCookie.parse getting passed the same long multi-cookie string as before?

Also, how are you getting access to the Set-Cookie headers? The browser seems to block me from seeing them.

You guessed it - I am using React Native. I use httponly cookies with browsers to prevent against XSS attacks (I assume you're doing the same). React Native ignores httponly, likely because XSS attacks aren't a thing in React Native.

@nfriedly
Copy link
Owner

Try the for/of loop for me, if that doesn't work, then I'm not sure what to say :(

@sshymko
Copy link
Author

sshymko commented Mar 14, 2017

Hey,

My understanding is that response.headers.get() returns a concatenated comma-separated string (not useful in case of Set-Cookie header). However, response.headers.getAll() should return an array of individual values. It is a bug #12846 of React Native that getAll "glues" values.

@sshymko
Copy link
Author

sshymko commented Mar 14, 2017

For the record: loop for..in returns the same combined header value as getAll() in React Native.

@nfriedly
Copy link
Owner

nfriedly commented Mar 14, 2017

Not for...in, for...of - they should have different behaviors.

Edit: I might be wrong there. Still worth a shot.

@sshymko
Copy link
Author

sshymko commented Mar 15, 2017

@nfriedly
Thanks for the tip! Tested both for...in and for...of on the latest React Native v0.42, iOS 10.2. The first one results in 0 iterations, the latter one returns the combined header value.

@sshymko
Copy link
Author

sshymko commented Mar 15, 2017

@nfriedly
Thank you very much for reaching out to whatwg/fetch for an advice.
The discussion there was eye-opening in terms of forbidding access to Set-Cookie headers.
That said, feel free to close the issue.
Thanks again.

This was referenced Jun 6, 2018
@nfmshow
Copy link

nfmshow commented Dec 30, 2019

I found this discussion really helpful

@ceciliazcx
Copy link

Is there any magic that everyone can get Set-Cookie field except me?

@nfriedly
Copy link
Owner

nfriedly commented Mar 17, 2020

@ceciliazcx The short version is that access to the set-cookie header is not allowed in the fetch spec, but Node.js and React Native go ahead and allow it anyways. If you're not in one of those two environments, though, you shouldn't expect to have access to the header. (To be clear, "regular" React running in a browser will not have access to the set-cookie header from a fetch request.)

This library was intended for server side usage in Node.js, and then extended to work with React Native. You might be able to get it to work in other contexts, but it's not a guarantee.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants