ardatan / whatwg-node

Helper packages to create platform agnostic applications and libraries without worrying about the lack of WHATWG support in Node.js
MIT License
158 stars 31 forks source link

Invalid value "undefined" for header "set-cookie" #509

Closed enri90 closed 1 year ago

enri90 commented 1 year ago

if i use res.setHeader('Set-Cookie', serializedCookies); it works

export interface ServerContext {
  req: IncomingMessage;
  res: ServerResponse;
} 

createYoga<ServerContext>

export function setCookies(res: ServerResponse, cookies: Cookie[]) {
  const options: CookieSerializeOptions = {
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
  };

  const serializedCookies = cookies.map((cookie) =>
    serialize(cookie.name, cookie.value, { ...options, ...cookie.options })
  );
  res.setHeader('Set-Cookie', serializedCookies);
}

instead through your plugin it error

 await ctx.request.cookieStore?.set('token', jwt);

TypeError: Invalid value "undefined" for header "set-cookie"
    at storeHeader (node:_http_outgoing:591:5)
    at processHeader (node:_http_outgoing:586:3)
    at ServerResponse._storeHeader (node:_http_outgoing:476:11)
    at ServerResponse.writeHead (node:_http_server:417:8)
    at sendNodeResponse (/Users/enri/Code/Progetti/vite/crm-vite/graphql-server/node_modules/@whatwg-node/server/cjs/utils.js:184:20)
    at requestListener (/Users/enri/Code/Progetti/vite/crm-vite/graphql-server/node_modules/@whatwg-node/server/cjs/createServerAdapter.js:102:51)
[ERROR] 00:55:21 TypeError: Invalid value "undefined" for header "set-cookie"
ardatan commented 1 year ago

Please create a reproduction on CodeSandbox or StackBlitz,.

jnssnmrcs commented 1 year ago

getting same error when using whatwg-node/server-plugin-cookies with graphql-yoga, i'm not even setting any cookie just including useCookies() in graphql-yoga plugins array, then I get this error when loading graphiql in browser:

TypeError: Invalid value "undefined" for header "set-cookie"
    at storeHeader (node:_http_outgoing:577:5)
    at processHeader (node:_http_outgoing:572:3)
    at ServerResponse._storeHeader (node:_http_outgoing:448:11)
    at ServerResponse.writeHead (node:_http_server:416:8)
    at sendNodeResponse (C:\Users\marcu\projects\cookbook\node_modules\.pnpm\@whatwg-node+server@0.7.5\node_modules\@whatwg-node\server\cjs\utils.js:184:20)
    at requestListener (C:\Users\marcu\projects\cookbook\node_modules\.pnpm\@whatwg-node+server@0.7.5\node_modules\@whatwg-node\server\cjs\createServerAdapter.js:102:51)        
    at processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_HTTP_INVALID_HEADER_VALUE
jnssnmrcs commented 1 year ago

from graphql-yoga docs comments:

image

https://github.com/marcusbesjes/yoga-cookies

enri90 commented 1 year ago

Then checking carefully the plugin presents various bugs

request.cookieStore.cookieString is undefined In the useCookies function in onResponse request.cookieStore.cookieString is undefined by setting the condition the error message mentioned above disappears

 request.cookieStore.cookieString !== ''
import { CookieStore } from '@whatwg-node/cookie-store';
import { ServerAdapterPlugin } from '@whatwg-node/server';

declare global {
  interface Request {
    cookieStore?: CookieStore;
  }
}

export function useCookies<TServerContext>(): ServerAdapterPlugin<TServerContext> {
  return {
    onRequest({ request }) {
      request.cookieStore = new CookieStore(request.headers.get('cookie') || '');
    },
    onResponse({ request, response }) {
      if (request.cookieStore && request.cookieStore.cookieString !== '') {
          response.headers.set('set-cookie', request.cookieStore.cookieString);
      }
    },
  };
}

Ability to enter all subdomains Domain=.test.local;

If I use more cookies in the request it doesn't work it only reads the first the second is not visible there is some error in cookie serialization

set-cookie: token=TOKEN; Domain=.test.local; Path=/; Expires=Tue, 09 May 2023 04:22:05 GMT; SameSite=Strict; refreshToken=ba001321-41ca-47ca-bff7-b0b1214750ad; Domain=.test.local; Path=/; Expires=Fri, 19 May 2023 02:22:05 GMT; SameSite=Strict

and for the expiration date of the cookie it util to enter the expiration minutes as in the npm cookie https://www.npmjs.com/package/cookie

edvinerikson commented 1 year ago

I created my own plugin which fixes this issue as well as another one where the server sends the set-cookie header for cookies that hasn't been changed.

import { Plugin } from 'graphql-yoga';
import { CookieStore, CookieList } from '@whatwg-node/cookie-store';

declare global {
  interface Request {
    cookieStore?: CookieStore;
  }
}

export function cookiePlugin() {
  const cache = new WeakMap<Request, CookieList>();

  const cookiePlugin: Plugin = {
    onRequest({ request }) {
      const store = new CookieStore(request.headers.get('cookie') || '');
      request.cookieStore = store;
      const cookies: CookieList = [];
      cache.set(request, cookies);
      store.onchange = (event) => {
        cookies.push(...event.changed, ...event.deleted);
      };
    },
    async onResponse({ response, request }) {
      const cookies = cache.get(request);
      if (cookies) {
        const store = new CookieStore('');
        for (const cookie of cookies) {
          await store.set(cookie);
        }
        response.headers.set('set-cookie', store.cookieString);
      }
    },
  };
  return cookiePlugin;
}
ardatan commented 1 year ago

Fixed in the latest release! Thanks for the feedback!

edvinerikson commented 1 year ago

@ardatan I still want to highlight that the plugin sets cookies incorrectly. When the browser sends a cookie like _ga=123456; the server will always send the set-cookie header with _ga=123456;. This means that whatever properties that was initially specified for that cookie will now be reset/removed. Eg if the cookie had Expires, it will now be reset to be a session cookie.

So if the server initially sets a cookie and the user reload, the server will set that cookie again but with default values.

enri90 commented 1 year ago

the problem of subdomains still remains

Cookie domain cannot start with "."


TypeError: Cookie domain cannot start with "."
    at CookieStore.set (/server/node_modules/@whatwg-node/cookie-store/cjs/CookieStore.js:47:23)
    at setCookies (/server/src/modules/builder/utils/cookie.ts:17:36)
    at resolve (/server/src/modules/auth/auth.mutation.ts:97:25)
    at /server/node_modules/@pothos/plugin-errors/src/index.ts:214:17
    at /server/node_modules/@envelop/core/cjs/orchestrator.js:376:27
    at YogaServer.getResultForParams (/server/node_modules/graphql-yoga/cjs/server.js:331:26)
    at YogaServer.handle (/server/node_modules/graphql-yoga/cjs/server.js:78:29)
    at handlerWithErrorHandling (/server/node_modules/@whatwg-node/server/cjs/plugins/useErrorHandling.js:33:38)
    at handleRequest (/server/node_modules/@whatwg-node/server/cjs/createServerAdapter.js:73:24)
    at requestListener (/server/node_modules/@whatwg-node/server/cjs/createServerAdapter.js:100:26)
ardatan commented 1 year ago

Ok if you have differnt problems, please create new issues with reproduction. Thanks!