-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
can no longer return multiple set-cookie
headers from endpoints
#3460
Comments
It looks like this issue goes all the way to the Is there any prospect for fixing this relatively soon, or is it going to be difficult to find a workaround? It breaks the JWT auth flow I've been using (and that I suspect various other people are), so I have to hold off updating my app until there's a fix. |
I haven't tested this, but I believe you can use this as a workaround:
|
No, that doesn't work because of the issue with the |
There should be a way to fix this server-side by using the if (headers.has('set-cookie')) {
const setCookieHeaders = headers.raw()['set-cookie'];
...
} |
It should be. I tracked down the problem on the kit source code, and found that the 'set-cookit' header (when is a Array) is flattened to a single string inside the setResponse function (packages/kit/src/node.js): res.writeHead(response.status, Object.fromEntries(response.headers)); That What I do not understand yet, is that .raw() is not available on the response object inside that function. That would be a quick fix. That's the first time that I opened SvelteKit source, so i'm sorry to be a little confuse.... I will try to fix that as a exercise :) |
This seems to get the job done: res.writeHead(response.status, getHeaders(response.headers));
function getHeaders(headers) {
const obj = Object.fromEntries(headers);
if (headers.has('set-cookie')) {
obj['set-cookie'] = headers.raw()['set-cookie'];
}
return obj;
} If we want to not overwrite the function getHeaders(headers) {
if (headers.has('set-cookie')) {
const obj = {};
for (const [header, value] of headers.entries()) {
obj[header] = header === 'set-cookie'
? headers.raw()[header]
: value;
}
return obj;
} else {
return Object.fromEntries(headers);
}
} We can't simply replace |
I almost did the same thing... /** @type {import('@sveltejs/kit/node').GetHeaders} */
export function getHeaders(response) {
let headers;
if (response.headers.has('set-cookie')) {
const cookieArray = response.headers.get('set-cookie')?.split(', ') || [];
headers = Object.assign(Object.fromEntries(response.headers), {'set-cookie': cookieArray});
} else {
headers = Object.fromEntries(response.headers);
}
return headers;
}
.....
/** @type {import('@sveltejs/kit/node').SetResponse} */
export async function setResponse(res, response) {
res.writeHead(response.status, getHeaders(response));
if (response.body instanceof Readable) {
response.body.pipe(res);
} else {
if (response.body) {
res.write(await response.arrayBuffer());
}
res.end();
}
} And... on packagets/kit/src/core/preview/index.js: import { getRawBody, getHeaders } from '../../node.js';
.....
res.writeHead(rendered.status, getHeaders(rendered)); |
The problem that i had here with that approach, was that .raw() don´t exist on the response.header object.... export async function setResponse(res, response) {
// testing raw()...
response.headers.raw()['set-cookie']
ERROR =>
Property 'raw' does not exist on type 'Headers'.ts(2339) Thats why I had to use split... ugly as hell. |
It worked, anyway... but I feel that there is a much cleaner way to solve this bug. |
Using split doesn't work if one of the cookie strings contains a colon inside itself. |
Definitely. Using split is not just ugly, it's not correct. Using raw() worked now. I confess that I don't remember what I was doing wrong. I created a helper function on utils/http.js (don´t know if it´s the best place) /** @param {Partial<import('@sveltejs/kit/install-fetch').Response>} response */
export function to_out_headers(response) {
if (!response?.headers) {
return new Headers();
};
if (response.headers.has('set-cookie')) {
return Object.assign(Object.fromEntries(response.headers), {'set-cookie': response.headers.raw()['set-cookie']});
} else {
return Object.fromEntries(response.headers);
}
} There are two places with this bug, as far as I know, dev and preview. I'm using this helper on both and now it looks like it's gone. |
Looks good to me. |
Just recapping parallel discussion happening in Discord:
|
Thanks for the update @Rich-Harris and glad that the team is thinking through this! It's weird that I wonder if this is the only complication you'll run into with using the Fetch API? |
Fingers crossed. At the very least we'd be in good company, since more platforms and frameworks are moving in this direction |
…veltejs#3460 * [fix] can no longer return multiple set-cookie headers from endpoints
* [fix] can no longer return multiple set-cookie headers from endpoints #3460 * [fix] can no longer return multiple set-cookie headers from endpoints * adding changeset * simplify * lint * simplify * tidy up and simplify test * tweak changeset Co-authored-by: Rich Harris <[email protected]>
After that fix multiple cookies works only in dev (npm run dev) or preview mode (npm run preview), but when I run it from build with node adapter (node build/index.js), then it is again just one long string with comma separated. "@sveltejs/kit": "^1.0.0-next.240" |
I think that might be because I forgot to add changesets for adapter-node/netlify, meaning they didn't get rebuilt with the fix. Releasing new versions now |
closing this as it appears to be fixed |
This works in endpoints, but unfortunately it still doesn't work for in the hooks.
Only the first item in the setCookies array gets assigned. Should we re-open this issue? |
Correct me if I'm wrong, but as far as I know you are not supposed to pass an array directly into for(const cookie of setCookies) {
response.headers.append("set-cookie", cookie)
} |
Hi, I have the same issue with multiple set-cookie. Before last framework update (234), everything worked well. Here is the link on stack : Link on stack I'm blocked and can't move on anymore on my App. Thanks for your help. Fabien |
Yes. You could use multiple appends. version: 1.0.0-next.252 export async function handle({ event, resolve }) {
const response = await resolve(event);
response.headers.append('Set-Cookie', 'handleFoo=foo');
response.headers.append('Set-Cookie', 'handleBar=bar; HttpOnly=true');
return response;
} |
Hi, |
Hi, could you please send the error logs? They give important data about the error and make the resolution process faster. |
Hi, Hope it can help ! |
Hi, |
Sorry for taking so long, I cannot reproduce your issue, have you tried to reproduce the error inside another endpoint? Noting that it is recommended to send a file with the relevant parts of the code related to the error when facing this kind of situation. |
No I did not tried to access it from another end point. |
Yes, I would like to see the repo. |
Invite sent ! edit: You'll find the main svelte directory here : Lionjar / sites / svelte-frontend |
I think the issue happened because you were using the spread operator ( To solve the issue I'd recommend modifying the response headers as bellow: if (user || loggingOut) {
response.headers.append("Set-Cookie", setCookieValue)
response.headers.append("Set-Cookie", setJwtCookieValue)
response.headers.append("Set-Cookie", setCompIdCookieValue)
}
return response |
Describe the bug
In a recent update, probably #3384, the ability to return multiple
set-cookie
headers from an endpoint seems to have broken. Instead of returning multipleset-cookie
headers, it returns just one with cookie values comma-separated (which browsers cannot interpret correctly).Reproduction
This endpoint:
produces the following headers:
but it should produce:
Logs
No response
System Info
Severity
blocking an upgrade
Additional Information
No response
The text was updated successfully, but these errors were encountered: