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

Set-Cookie response headers lose semantics #309

Closed
dominique-pfister opened this issue Oct 11, 2022 · 8 comments
Closed

Set-Cookie response headers lose semantics #309

dominique-pfister opened this issue Oct 11, 2022 · 8 comments
Labels
enhancement New feature or request released

Comments

@dominique-pfister
Copy link
Contributor

dominique-pfister commented Oct 11, 2022

Description
When a server returns more than one set-cookie response headers, the response.headers.get('set-cookie') will return one value, where all values are concatenated by commas.

To Reproduce
Steps to reproduce the behavior:

  1. Fetch a URL from a server that returns multiple set-cookie headers, e.g. https://www.dropbox.com
  2. Look at the value of response.headers.get('set-cookie')
  3. When the response looks like this in curl:
set-cookie:  gvc=MjM0MDU4MTU5Nzg3MzE0MzE2Nzk4NTk1MDA0NDkxMTk5NTEzNTc2; expires=Sun, 10 Oct 2027 13:36:25 GMT; HttpOnly; Path=/; SameSite=None; Secure
set-cookie:  t=lTCMMujd5gvO17wv9zPLRrvC; Domain=dropbox.com; expires=Fri, 10 Oct 2025 13:36:24 GMT; HttpOnly; Path=/; SameSite=None; Secure
set-cookie:  __Host-js_csrf=lTCMMujd5gvO17wv9zPLRrvC; expires=Fri, 10 Oct 2025 13:36:24 GMT; Path=/; SameSite=None; Secure
set-cookie:  __Host-ss=jtJon8XHjQ; expires=Fri, 10 Oct 2025 13:36:24 GMT; HttpOnly; Path=/; SameSite=Strict; Secure
set-cookie:  locale=en; Domain=dropbox.com; expires=Sun, 10 Oct 2027 13:36:25 GMT; Path=/; SameSite=None; Secure

you'll notice that response.headers.get('set-cookie') looks like this (line feeds added for readibility):

gvc=MjM0MDU4MTU5Nzg3MzE0MzE2Nzk4NTk1MDA0NDkxMTk5NTEzNTc2; 
expires=Sun, 10 Oct 2027 13:36:25 GMT; HttpOnly; Path=/; SameSite=None;
Secure,t=lTCMMujd5gvO17wv9zPLRrvC; ... (rest omitted)

Using a standard cookie parser, it will report that there's a Secure,t cookie, because it is unable to determine that the former is a flag for the first gvc cookie.

Expected behavior
It should be possible to parse the cookies received, either by getting the initial N values for that header or by using a different delimiter.

@dominique-pfister dominique-pfister added the bug Something isn't working label Oct 11, 2022
@dominique-pfister
Copy link
Contributor Author

dominique-pfister commented Oct 11, 2022

Doing some debugging reveals that the concatenation happens in

fetch/src/fetch/headers.js

Lines 104 to 109 in c55384f

append(name, value) {
const nm = normalizeName(name);
const val = normalizeValue(value, name);
const oldVal = this[INTERNALS].map.get(nm);
this[INTERNALS].map.set(nm, oldVal ? `${oldVal}, ${val}` : val);
}

namely in normalizeValue:
const normalizeValue = (value, name) => {
const val = typeof value !== 'string' ? String(value) : value;

The explicit String cast turns the [5] array in the example above into a string concatenated with commas.

@dominique-pfister
Copy link
Contributor Author

Note that the fetch living standard says the following:

headers . get(name)
Returns as a string the values of all headers whose name is name, separated by a comma and a space.

https://fetch.spec.whatwg.org/#ref-for-dom-headers-get%E2%91%A0

@tripodsan
Copy link
Contributor

tripodsan commented Oct 11, 2022

the problem with the , above, even if done correctly with the space after the comma, is that if the header value contains a ,, splitting is difficult again.

I think it would be better / useful to receive all values of a multi-header header via api. eg:

response.headers.getAll(name) -> string[]

or entries() could return the header multiple times`

response.headers.entries() -> [ ['set-cookie', 'a'], ['set-cookie', 'b'] ]

or exposing https://fetch.spec.whatwg.org/#concept-header-list

response.headerList -> ?

@stefan-guggisberg
Copy link
Contributor

stefan-guggisberg commented Oct 11, 2022

Known issue with Fetch spec, see e.g.

https://stackoverflow.com/questions/63204093/how-to-get-set-multiple-set-cookie-headers-using-fetch-api
whatwg/fetch#973
node-fetch/node-fetch#251
node-fetch/node-fetch#582

@tripodsan
Copy link
Contributor

as far as I understand, https://github.com/node-fetch/node-fetch/pull/253/files adds multi-headers as array for headers.raw(). so this could be done here as well for headers.plain() ?

@stefan-guggisberg
Copy link
Contributor

so this could be done here as well for headers.plain() ?

yes, that's the plan. need to change the internal representation though.

@stefan-guggisberg stefan-guggisberg added enhancement New feature or request and removed bug Something isn't working labels Oct 13, 2022
@stefan-guggisberg
Copy link
Contributor

BTW

The explicit String cast turns the [5] array in the example above into a string concatenated with commas.

That's in fact exactly what the browser (Chrome) does:

const h = new Headers()
undefined
h.append('x-cookie', ['foo=1', 'bar=2'])
undefined
h.get('x-cookie')
'foo=1,bar=2'

stefan-guggisberg added a commit that referenced this issue Oct 13, 2022
github-actions bot pushed a commit that referenced this issue Oct 13, 2022
# [3.2.0](v3.1.4...v3.2.0) (2022-10-13)

### Features

* support raw Set-Cookie headers ([#310](#310)) ([d5c80aa](d5c80aa)), closes [#309](#309)
@github-actions
Copy link

🎉 This issue has been resolved in version 3.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request released
Projects
None yet
Development

No branches or pull requests

3 participants