Using multiValueHeaders to return multiple cookies works in local development but not in production

Hi,

I am using multiValueHeaders to return multiple cookies like this:

...
  const sessionCookie = cookie.serialize('_auth', token, {
    secure: process.env.ENV === 'production',
    domain: process.env.ENV  === 'production' ? '.mydomain.com' : 'localhost',
    httpOnly: true,
    path: '/',
    maxAge: 3600
  })
...
  return {
    'statusCode': 200,
    'multiValueHeaders': {
      'Set-Cookie': [sessionCookie, rbacCookie]
    },
    'headers': {
      'Cache-Control': 'no-cache',
    },
    'body': JSON.stringify(body)
  }

It works locally using netlify dev, however, when I deploy the application it does not return any cookies.

If I return a single cookie in the headers, like this:

  return {
    'statusCode': 200,
    'headers': {
      'Set-Cookie': sessionCookie
      'Cache-Control': 'no-cache',
    },
    'body': JSON.stringify(body)
  }

It works fine in production. However, you can’t return multiple cookies this way as it throws an error.

I have trawled the docs and support forums, but I can’t resolve it.

Do you have any ideas why multiValueHeaders would not work in production or am I missing something obvious?

Thanks,

Paul

Hi @PDS

According to the Passing multiple Set-Cookie headers in the response section of Support for multi-value parameters in Amazon API Gateway | AWS Compute Blog you need to use multiValueHeaders instead of headers

const response = {
  isBase64Encoded: true,
  statusCode: 200,
  multiValueHeaders : {
    "Set-Cookie": [
      `language=${language}`,
      `theme=${theme}`
    ]
  },
  body: JSON.stringify('User profile set successfully')
};

(Example from page.)

Though this looks similar to what you have…
I haven’t had need to try this myself, but am interested to see if this works for you.

Edit:
After deploying a simple test, this is the result I see

Hi @coelmay

Thanks for the swift reply.

I did try:

  return {
    'statusCode': 200,
    'multiValueHeaders': {
      'Cache-Control': 'no-cache',
      'Set-Cookie': [sessionCookie, rbacCookie]
    },
    'body': JSON.stringify(body)
  }

However, that returns a 502 in production, but still works ok locally.

Wrapping all headers in an array, again works locally, but in production, although it does not return a 502, it still does not return the cookies.

  return {
    'statusCode': 200,
    'multiValueHeaders': {
      'Cache-Control': ['no-cache'], <<<<<< pass value in array
      'Set-Cookie': [sessionCookie, rbacCookie]
    },
    'body': JSON.stringify(body)
  }

I picked up in this post that @ekafyi resolved his issue using both headers and multiValueHeaders:

" multiValueHeaders can only contain the Set-Cookie property. Anything else goes in headers as usual."

Which or course I tried, but had not joy.

Thanks,

Paul

A little more tinkering and I get this…

This is the function I used

const cookie = require('cookie')
exports.handler = async (event, context, callback) => {

  var language = "Toki Pona";
  var theme = "Orange";

  const sessionCookie = cookie.serialize('_auth', "KJASDIU34567ASKDGA", {
    // secure: process.env.ENV === 'production',
    domain: 'boring-joliot-18e93d.netlify.app',
    httpOnly: true,
    path: '/',
    maxAge: 3600
  });

  console.log("Session Cookie %s",sessionCookie);
  
  return {
    // isBase64Encoded: true,
    statusCode: 200,
    headers: {
      "Cache-Control": "no-cache"
    },
    multiValueHeaders : {
      "Set-Cookie": [
        `language=${language}`,
        `theme=${theme}`,
        `sessionCookie=${JSON.stringify(sessionCookie)}`,
      ]
    },
    body: JSON.stringify(`'User profile set successfully'`)
  };
};

You have got me closer to working it out :+1:

If I simply add string values for the cookies:

    multiValueHeaders: {
      'Set-Cookie': [
        'nf_jwt=token',
        '_auth=token'
      ]
    }

It works in production as expected by sending two cookies.

So it has to do with how I am serializing the cookie tokens.

It is a quite late my side, so I will work through it tomorrow. When I get it working I’ll post my result or mark your previous post as the solution.

I can’t thank you enough for your help - I was running out of steam.

Thanks,

Paul

1 Like

One issue is process.env.ENV is meant to read process.env.NODE_ENV.

And as I was reminded (I just never learn sometimes!) after a little searching

So you need to set NODE_ENV in the Netlify UI to production.

This combined with the const sessionCooke = ... you have and the multiValueHeaders will work.

Example:

const cookie = require('cookie')
exports.handler = async (event, context, callback) => {

  var token = "KJASDIU34567ASKDGA"
  const sessionCookie = cookie.serialize('_auth', token, {
    secure: process.env.NODE_ENV === 'production',
    domain: process.env.NODE_ENV  === 'production' ? '.mydomain.com' : 'localhost',
    httpOnly: true,
    path: '/',
    maxAge: 3600
  });

  return {
    statusCode: 200,
    headers: {
      "Cache-Control": "no-cache"
    },
    multiValueHeaders : {
      "Set-Cookie": [
        'language=Dansk',
        sessionCookie
      ]
    },
    body: JSON.stringify(event.body)
  };
};

Learned more about cookies today than any previous day of my life. Still prefer chocolate chip though :smiley:

1 Like

@coelmay :clap:

Thank you. I indeed had an issue with the environment variables. I had changed them in my .env file and the portal to suit using a vite app, which require a prefix, and did not update the code. It seemed to fail silently and took a bit of trial and error to uncover that.

I have marked your answer as the solution as anyone following that example will have a good base to work from.

It is worth noting for anyone else reviewing this that the line:

domain: process.env.NODE_ENV === 'production' ? '.mydomain.com' : 'localhost',

is to allow the cookie to be used across subdomains.

I also learned a lot about cookies today and I agree - the edible ones are much better :cookie:.

Thanks again.

Paul

1 Like

Eat cookie like this? :rofl:

giphy

2 Likes