diff --git a/.changeset/olive-dogs-bathe.md b/.changeset/olive-dogs-bathe.md new file mode 100644 index 000000000000..1d0f193ac71c --- /dev/null +++ b/.changeset/olive-dogs-bathe.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: stop setting Kit cookie defaults on cookies parsed from headers diff --git a/packages/kit/src/runtime/server/cookie.js b/packages/kit/src/runtime/server/cookie.js index f8d872207005..1ca4b5b15b5c 100644 --- a/packages/kit/src/runtime/server/cookie.js +++ b/packages/kit/src/runtime/server/cookie.js @@ -107,32 +107,7 @@ export function get_cookies(request, url, trailing_slash) { * @param {import('cookie').CookieSerializeOptions} opts */ set(name, value, opts = {}) { - let path = opts.path ?? default_path; - - new_cookies[name] = { - name, - value, - options: { - ...defaults, - ...opts, - path - } - }; - - if (__SVELTEKIT_DEV__) { - const serialized = serialize(name, value, new_cookies[name].options); - if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) { - throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`); - } - - cookie_paths[name] ??= new Set(); - - if (!value) { - cookie_paths[name].delete(path); - } else { - cookie_paths[name].add(path); - } - } + set_internal(name, value, { ...defaults, ...opts }); }, /** @@ -193,7 +168,40 @@ export function get_cookies(request, url, trailing_slash) { .join('; '); } - return { cookies, new_cookies, get_cookie_header }; + /** + * @param {string} name + * @param {string} value + * @param {import('cookie').CookieSerializeOptions} opts + */ + function set_internal(name, value, opts) { + let path = opts.path ?? default_path; + + new_cookies[name] = { + name, + value, + options: { + ...opts, + path + } + }; + + if (__SVELTEKIT_DEV__) { + const serialized = serialize(name, value, new_cookies[name].options); + if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) { + throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`); + } + + cookie_paths[name] ??= new Set(); + + if (!value) { + cookie_paths[name].delete(path); + } else { + cookie_paths[name].add(path); + } + } + } + + return { cookies, new_cookies, get_cookie_header, set_internal }; } /** diff --git a/packages/kit/src/runtime/server/cookie.spec.js b/packages/kit/src/runtime/server/cookie.spec.js index e0dfdc362784..d121a3022e40 100644 --- a/packages/kit/src/runtime/server/cookie.spec.js +++ b/packages/kit/src/runtime/server/cookie.spec.js @@ -195,4 +195,19 @@ test('get all cookies from header and set calls', () => { ]); }); +test("set_internal isn't affected by defaults", () => { + const { cookies, new_cookies, set_internal } = cookies_setup({ + href: 'https://example.com/a/b/c' + }); + const options = /** @type {const} */ ({ + secure: false, + httpOnly: false, + sameSite: 'none', + path: '/a/b/c' + }); + set_internal('test', 'foo', options); + assert.equal(cookies.get('test'), 'foo'); + assert.equal(new_cookies['test']?.options, options); +}); + test.run(); diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js index ca5df895f9a8..234da0eb82a4 100644 --- a/packages/kit/src/runtime/server/fetch.js +++ b/packages/kit/src/runtime/server/fetch.js @@ -9,10 +9,11 @@ import * as paths from '__sveltekit/paths'; * manifest: import('types').SSRManifest; * state: import('types').SSRState; * get_cookie_header: (url: URL, header: string | null) => string; + * set_internal: (name: string, value: string, opts: import('cookie').CookieSerializeOptions) => void; * }} opts * @returns {typeof fetch} */ -export function create_fetch({ event, options, manifest, state, get_cookie_header }) { +export function create_fetch({ event, options, manifest, state, get_cookie_header, set_internal }) { return async (info, init) => { const original_request = normalize_fetch_input(info, init, event.url); @@ -131,7 +132,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade const { name, value, ...options } = set_cookie_parser.parseString(str); // options.sameSite is string, something more specific is required - type cast is safe - event.cookies.set( + set_internal( name, value, /** @type {import('cookie').CookieSerializeOptions} */ (options) diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js index b29e2c6484ed..a82f48ce1111 100644 --- a/packages/kit/src/runtime/server/respond.js +++ b/packages/kit/src/runtime/server/respond.js @@ -244,7 +244,7 @@ export async function respond(request, options, manifest, state) { } } - const { cookies, new_cookies, get_cookie_header } = get_cookies( + const { cookies, new_cookies, get_cookie_header, set_internal } = get_cookies( request, url, trailing_slash ?? 'never' @@ -252,7 +252,14 @@ export async function respond(request, options, manifest, state) { cookies_to_add = new_cookies; event.cookies = cookies; - event.fetch = create_fetch({ event, options, manifest, state, get_cookie_header }); + event.fetch = create_fetch({ + event, + options, + manifest, + state, + get_cookie_header, + set_internal + }); if (state.prerendering && !state.prerendering.fallback) disable_search(url);