diff --git a/.changeset/six-rules-collect.md b/.changeset/six-rules-collect.md new file mode 100644 index 000000000000..fd516d12ecaa --- /dev/null +++ b/.changeset/six-rules-collect.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Pass through credentials when fetching in load diff --git a/packages/kit/package.json b/packages/kit/package.json index 028f27b3e4b7..676a73655dd2 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -15,6 +15,7 @@ "@types/rimraf": "^3.0.0", "@types/sade": "^1.7.2", "amphtml-validator": "^1.0.34", + "cookie": "^0.4.1", "devalue": "^2.0.1", "eslint": "^7.21.0", "kleur": "^4.1.4", diff --git a/packages/kit/src/runtime/server/page.js b/packages/kit/src/runtime/server/page.js index f946f8ffaf34..3badbfff77ac 100644 --- a/packages/kit/src/runtime/server/page.js +++ b/packages/kit/src/runtime/server/page.js @@ -80,11 +80,6 @@ async function get_response({ request, options, $session, route, status = 200, e const parsed = parse(url); - // TODO: fix type https://github.com/node-fetch/node-fetch/issues/1113 - if (opts.credentials !== 'omit') { - uses_credentials = true; - } - let response; if (parsed.protocol) { @@ -121,11 +116,24 @@ async function get_response({ request, options, $session, route, status = 200, e } if (!response) { + const headers = /** @type {import('types.internal').Headers} */ ({ ...opts.headers }); + + // TODO: fix type https://github.com/node-fetch/node-fetch/issues/1113 + if (opts.credentials !== 'omit') { + uses_credentials = true; + + headers.cookie = request.headers.cookie; + + if (!headers.authorization) { + headers.authorization = request.headers.authorization; + } + } + const rendered = await ssr( { host: request.host, method: opts.method || 'GET', - headers: /** @type {import('types.internal').Headers} */ (opts.headers || {}), // TODO inject credentials... + headers, path: resolved, body: /** @type {any} */ (opts.body), query: new URLSearchParams(parsed.query || '') @@ -159,7 +167,7 @@ async function get_response({ request, options, $session, route, status = 200, e /** @type {import('types.internal').Headers} */ const headers = {}; response.headers.forEach((value, key) => { - if (key !== 'etag') headers[key] = value; + if (key !== 'etag' && key !== 'set-cookie') headers[key] = value; }); // prettier-ignore diff --git a/packages/kit/test/apps/basics/src/hooks.js b/packages/kit/test/apps/basics/src/hooks.js index 6dde9e6ba1d3..b3168c1be700 100644 --- a/packages/kit/test/apps/basics/src/hooks.js +++ b/packages/kit/test/apps/basics/src/hooks.js @@ -1,10 +1,31 @@ -export function getContext() { +import cookie from 'cookie'; + +/** @type {import('../../../../types').GetContext} */ +export function getContext(request) { + const cookies = cookie.parse(request.headers.cookie || ''); + return { - answer: 42 + answer: 42, + name: cookies.name }; } -/** @param {any} context */ +/** @type {import('../../../../types').GetSession} */ export function getSession({ context }) { return context; } + +/** @type {import('../../../../types').Handle} */ +export async function handle(request, render) { + const response = await render(request); + + if (response) { + return { + ...response, + headers: { + ...response.headers, + 'Set-Cookie': 'name=SvelteKit; path=/; HttpOnly' + } + }; + } +} diff --git a/packages/kit/test/apps/basics/src/routes/load/__tests__.js b/packages/kit/test/apps/basics/src/routes/load/__tests__.js index aac0263a6ed6..ae658a491804 100644 --- a/packages/kit/test/apps/basics/src/routes/load/__tests__.js +++ b/packages/kit/test/apps/basics/src/routes/load/__tests__.js @@ -133,4 +133,13 @@ export default function (test, is_dev) { server.close(); }); + + test( + 'makes credentialed fetches to endpoints by default', + '/load', + async ({ page, clicknav }) => { + await clicknav('[href="/load/fetch-credentialed"]'); + assert.equal(await page.textContent('h1'), 'Hello SvelteKit!'); + } + ); } diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js new file mode 100644 index 000000000000..f92f473487e7 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.json.js @@ -0,0 +1,7 @@ +export function get(request) { + return { + body: { + name: request.context.name + } + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte new file mode 100644 index 000000000000..65ae70f8854c --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-credentialed.svelte @@ -0,0 +1,22 @@ + + + + +

Hello {name}!

\ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/load/index.svelte b/packages/kit/test/apps/basics/src/routes/load/index.svelte index 8ba59dcdf1ff..cadb56255838 100644 --- a/packages/kit/test/apps/basics/src/routes/load/index.svelte +++ b/packages/kit/test/apps/basics/src/routes/load/index.svelte @@ -19,4 +19,5 @@

bar == {foo}?

fetch request +fetch credentialed large response diff --git a/packages/kit/test/test.js b/packages/kit/test/test.js index 0498932b3a32..36e1d73838ce 100644 --- a/packages/kit/test/test.js +++ b/packages/kit/test/test.js @@ -10,6 +10,8 @@ import { load_config } from '../src/core/load_config/index.js'; import { fileURLToPath, pathToFileURL } from 'url'; async function setup({ port }) { + const base = `http://localhost:${port}`; + const browser = await chromium.launch(); const contexts = { @@ -17,6 +19,17 @@ async function setup({ port }) { nojs: await browser.newContext({ javaScriptEnabled: false }) }; + const cookie = { + name: 'name', + value: 'SvelteKit', + domain: base, + path: '/', + httpOnly: true + }; + + await contexts.js.addCookies([cookie]); + await contexts.nojs.addCookies([cookie]); + const pages = { js: await contexts.js.newPage(), nojs: await contexts.nojs.newPage() @@ -49,7 +62,6 @@ async function setup({ port }) { return requests; }; - const base = `http://localhost:${port}`; // Uncomment this for debugging // pages.js.on('console', (msg) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8cea7e014c01..08c45e2b7cb5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,7 @@ importers: '@types/rimraf': 3.0.0 '@types/sade': 1.7.2 amphtml-validator: 1.0.34 + cookie: 0.4.1 devalue: 2.0.1 eslint: 7.21.0 kleur: 4.1.4 @@ -197,6 +198,7 @@ importers: '@types/sade': ^1.7.2 amphtml-validator: ^1.0.34 cheap-watch: ^1.0.3 + cookie: ^0.4.1 devalue: ^2.0.1 eslint: ^7.21.0 kleur: ^4.1.4 @@ -1187,7 +1189,6 @@ packages: resolution: integrity: sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= /cookie/0.4.1: - dev: false engines: node: '>= 0.6' resolution: