kit icon indicating copy to clipboard operation
kit copied to clipboard

Credentials example in handleFetch hook documentation is incorrect if a cookie was set by SvelteKit

Open knpwrs opened this issue 3 years ago • 3 comments

Describe the bug

The documentation for handleFetch provides the following example for forwarding cookies to an API on a different domain than SvelteKit is hosted at:

/** @type {import('@sveltejs/kit').[HandleFetch](https://kit.svelte.dev/docs/types#sveltejs-kit-handlefetch)} */
export async function handleFetch({ request, fetch }) {
  if (request.url.startsWith('https://api.yourapp.com/')) {
    // clone the original request, but change the URL
    request = new Request(
      request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
      request
    );
  }
 
  return fetch(request);
}

Based on that, I created the following hook to additionally forward cookies from the API server to the client (since it appears that cookies are not forwarded on their own):

import envariant from '@knpwrs/envariant';
import type { HandleFetch } from '@sveltejs/kit';

const GRAPHQL_URL = envariant('GRAPHQL_URL');
const COOKIE_KEY = 'lcSessionId';

export const handleFetch: HandleFetch = async ({ event, request, fetch }) => {
  const cookie = event.request.headers.get('cookie');

  if (request.url.startsWith(GRAPHQL_URL) && cookie) {
    request.headers.set('cookie', cookie);
  }

  const res = await fetch(request);

  const cookieValue = res.headers
    .get('set-cookie')
    ?.replace(`${COOKIE_KEY}=`, '');

  if (cookieValue) {
    event.cookies.set(COOKIE_KEY, cookieValue, {
      sameSite: 'lax',
      httpOnly: true,
      path: '/',
    });
  }

  return res;
};

The issue is that it appears that SvelteKit is URL-encoding the cookie value with event.cookies.set, but then not URL-decoding the cookie value when reading event.request.headers.get('cookie'). This makes sense as they're different APIs, but it leads to unintuitive bugs where a cookie set by SvelteKit is ultimately not sent to backend APIs in the same form it was set.

Reproduction

  1. Set a cookie with
  2. Attempt to forward that cookie using provided example in the documentation

Logs

No response

System Info

System:
    OS: macOS 12.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 82.08 MB / 32.00 GB
    Shell: 5.8 - /opt/homebrew/bin/zsh
  Binaries:
    Node: 18.10.0 - /nix/store/m3za3mblwg9az7vpkkvlfm04z66mcr6y-nodejs-18.10.0/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 8.19.2 - /nix/store/m3za3mblwg9az7vpkkvlfm04z66mcr6y-nodejs-18.10.0/bin/npm
    Watchman: 2022.10.03.00 - /opt/homebrew/bin/watchman
  Browsers:
    Brave Browser: 106.1.44.101
    Chrome: 107.0.5304.110
    Chrome Canary: 99.0.4828.0
    Firefox: 106.0.5
    Safari: 15.2
  npmPackages:
    @sveltejs/adapter-auto: ^1.0.0-next.88 => 1.0.0-next.88 
    @sveltejs/kit: ^1.0.0-next.548 => 1.0.0-next.548 
    svelte: ^3.53.1 => 3.53.1 
    vite: ^3.1.0 => 3.2.4

Severity

annoyance

Additional Information

IIRC it isn't possible to set the set-cookie header in SvelteKit. Maybe it should be possible to set this header in hooks but not in page/layout handlers? That way backend cookies can be forwarded directly.

knpwrs avatar Nov 17 '22 14:11 knpwrs

That example only uses the Header API, not the cookies API, so the example in the documentation should be correct. You are using a different API - the cookies API - afterwards to add a returned cookie back to the original event. This isn't part of the example in the docs.

If anything, we should probably enhance the example to show the way back, i.e. adding a cookie from the response back to the original event.

dummdidumm avatar Nov 18 '22 09:11 dummdidumm

Some sort of official documentation on forwarding cookies would be great! Something about my .replace on the value of set-cookie feels kinda off.

knpwrs avatar Nov 18 '22 15:11 knpwrs

This is still a major problem if you're trying to perform authentication requests (login/logout) to an external API. It could additionally open a major security hole by not respecting the original parameters that a cookie was set with, such as httpOnly or isSecure.

malammar avatar Aug 03 '23 18:08 malammar

I am trying to use django as an api for auth, right now and i am stuck. Is it even technically possible to make it work securely?

Ainekko avatar Sep 23 '23 21:09 Ainekko