koajs / session

Simple session middleware for koa

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

The cookie doesn't allow response on production environment

phivh opened this issue · comments

The scenario is when the backend service based Koajs and Koa-session with the default configuration:

app.keys = [process.env.SESSION_SECRET];
const isProd = process.env.NODE_ENV === 'production' ? true : false;
const SESSION_CONFIG: any = {
  key: 'koa:sess',
  maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
  overwrite: true,
  httpOnly: isProd,
  signed: true,
  rolling: true,
  renew: false,
  secure: isProd,
  sameSite: 'None',
};

When the request calling to the endpoint and get an error code 500, it doesn't respond as expected and set the cookie to the browser.
But when removing secure and sameSite, it's working but the cookie doesn't set to the browser.

I believe there are a couple of things you should consider to fix this issue. Do you intentionally want the cookie sent in a third-party context? If so you are correct to set the sameSite property to none, however, modern browsers will only set cookies that are marked with sameSite=none if they are also marked with secure. This could be one reason why your cookie is not being set in development when NODE_ENV does not equal production.

My best guess in why you are receiving an HTTP status code of 500 is koa-session is throwing the error Cannot send secure cookie over unencrypted connection.

To hopefully get this fixed can you provide more info about the environment you are using, are you testing locally on your development machine or on a live production server? Are you using HTTP or HTTPS requests?

I believe there are a couple of things you should consider to fix this issue. Do you intentionally want the cookie sent in a third-party context? If so you are correct to set the sameSite property to none, however, modern browsers will only set cookies that are marked with sameSite=none if they are also marked with secure. This could be one reason why your cookie is not being set in development when NODE_ENV does not equal production.

My best guess in why you are receiving an HTTP status code of 500 is koa-session is throwing the error Cannot send secure cookie over unencrypted connection.

To hopefully get this fixed can you provide more info about the environment you are using, are you testing locally on your development machine or on a live production server? Are you using HTTP or HTTPS requests?

Thanks for your answer.
I've tested under the local machine and it seems working and I mean here on the live production when
NODE_ENV equal to production as you can see my code here:

const isProd = process.env.NODE_ENV === 'production' ? true : false;

Under local development with the output:

const SESSION_CONFIG: any = {
 ...
  httpOnly: false,
  signed: true,
  rolling: true,
  renew: false,
  secure: false,
  sameSite: 'None',
};

On live production with the output:

const SESSION_CONFIG: any = {
 ...
  httpOnly: true,
  signed: true,
  rolling: true,
  renew: true,
  secure: true,
  sameSite: 'None',
};

And of course on live production with SSL request

I am going to assume you are running your application behind a proxy in production. Adding app.proxy = true should sort this issue out. The reason why you are having this issue is because the proxy is sending an HTTP request to the application and not the SSL HTTPS request, setting app.proxy = true tells Koa that when determining whether a request is secure it may trust the headers that the load-balancer or proxy adds to each request.

I have tested this solution on a local development machine and production server using Google Cloud Run (uses a reverse proxy) and had success with this simple test application:

'use strict'

const Koa = require('koa')
const session = require('koa-session')

const app = new Koa()

const isProd = process.env.NODE_ENV === 'production' ? true : false
const PORT = process.env.PORT || 3000

app.keys = [process.env.SESSION_SECRET]
app.proxy = true

const SESSION_CONFIG = {
  key: 'koa:sess',
  maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days
  overwrite: true,
  httpOnly: isProd,
  signed: true,
  rolling: true,
  renew: false,
  secure: isProd,
  sameSite: 'none'
}

app.use(session(SESSION_CONFIG, app))

app.use(ctx => {
  let views = ctx.session.views || 0
  ctx.session.views = ++views
  ctx.body = `${views} views`
})

app.listen(PORT)

Hope this helps 😄

Thanks for your solution, it seems the server allow when set proxy=true but the response with the cookie doesn't set to browser.
My origin request from requestdomain.com to api.domain.com and the server set a cookie to api.domain.com, not to the requestdomain.com. Although, sameSite=None

I am not sure if this stackoverflow answer could help you here. You would have to use ctx.cookies.set('domain', 'api.domain.com'). This gives me these cookies when adding this to the example I used above:

screenshot-Wed-30Dec20_17 59

Thanks @dominicegginton , The solution above was helpful and we also can set domain: 'api.domain.com' in the configuration. Maybe this is also a session to learn.
const SESSION_CONFIG = { key: 'koa:sess', maxAge: 1000 * 60 * 60 * 24 * 7, // 7 days overwrite: true, httpOnly: isProd, signed: true, rolling: true, renew: false, secure: isProd, sameSite: 'None', domain: 'api.anotherdomain.com', }
Thanks for your help!

No problem I am happy to help, your soultion of setting the domain value within the koa-session config I belive is a better way than the one I suggested, I will add this to the documentation when I have time.

Thanks all, I already fixed the issue.

I'll re-format the answer of @phivh to make it more readable

const isProd = process.env.NODE_ENV === 'production' ? true : false

const CONFIG = {
  key: 'koa.sess',
  maxAge: 86400000,
  autoCommit: true, 
  overwrite: true, 
  httpOnly: isProd, 
  signed: true, 
  rolling: false, 
  renew: false, 
  secure: isProd,
  sameSite: 'None',
};

app.use(koaSession(CONFIG, app));