From b8fbfcc903f23789b84ca09104dcb17a01736db7 Mon Sep 17 00:00:00 2001 From: djunehor Date: Mon, 10 Nov 2025 03:06:18 +0100 Subject: [PATCH 1/6] feat: add sameSite 'auto' support for automatic cross-site cookie configuration --- README.md | 9 +++++ index.js | 4 ++ test/session.js | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/README.md b/README.md index b880e6b4..4f19ce59 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ By default, this is `false`. - `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement. - `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie. - `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement. + - `'auto'` will set the `SameSite` attribute to `None` for secure connections and `Lax` for non-secure connections. More information about the different enforcement levels can be found in [the specification][rfc-6265bis-03-4.1.2.7]. @@ -141,6 +142,14 @@ the future. This also means many clients may ignore this attribute until they un that requires that the `Secure` attribute be set to `true` when the `SameSite` attribute has been set to `'none'`. Some web browsers or other clients may be adopting this specification. +The `cookie.sameSite` option can also be set to the special value `'auto'` to have +this setting automatically match the determined security of the connection. When the connection +is secure (HTTPS), the `SameSite` attribute will be set to `None` to enable cross-site usage. +When the connection is not secure (HTTP), the `SameSite` attribute will be set to `Lax` for +better security while maintaining functionality. This is useful when the Express `"trust proxy"` +setting is properly setup to simplify development vs production configuration, particularly +for SAML authentication scenarios. + ##### cookie.secure Specifies the `boolean` value for the `Secure` `Set-Cookie` attribute. When truthy, diff --git a/index.js b/index.js index 85a0330c..022a94fd 100644 --- a/index.js +++ b/index.js @@ -163,6 +163,10 @@ function session(options) { if (cookieOptions.secure === 'auto') { req.session.cookie.secure = issecure(req, trustProxy); } + + if (cookieOptions.sameSite === 'auto') { + req.session.cookie.sameSite = issecure(req, trustProxy) ? 'none' : 'lax'; + } }; var storeImplementsTouch = typeof store.touch === 'function'; diff --git a/test/session.js b/test/session.js index a7d79b70..ac45d88a 100644 --- a/test/session.js +++ b/test/session.js @@ -801,6 +801,108 @@ describe('session()', function(){ }) }) }) + + describe('when "sameSite" set to "auto"', function () { + describe('basic functionality', function () { + before(function () { + function setup (req) { + req.secure = JSON.parse(req.headers['x-secure']) + } + + function respond (req, res) { + res.end(String(req.secure)) + } + + this.server = createServer(setup, { cookie: { sameSite: 'auto' } }, respond) + }) + + it('should set SameSite=None for secure connections', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'true') + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, 'true', done) + }) + + it('should set SameSite=Lax for insecure connections', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, 'false', done) + }) + }) + + describe('with proxy settings', function () { + describe('when "proxy" is "true"', function () { + before(function () { + this.server = createServer({ proxy: true, cookie: { sameSite: 'auto' }}) + }) + + it('should set SameSite=None when X-Forwarded-Proto is https', function (done) { + request(this.server) + .get('/') + .set('X-Forwarded-Proto', 'https') + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, done) + }) + + it('should set SameSite=Lax when X-Forwarded-Proto is http', function (done) { + request(this.server) + .get('/') + .set('X-Forwarded-Proto', 'http') + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, done) + }) + }) + + describe('when "proxy" is "false"', function () { + before(function () { + this.server = createServer({ proxy: false, cookie: { sameSite: 'auto' }}) + }) + + it('should set SameSite=Lax when X-Forwarded-Proto is https', function (done) { + request(this.server) + .get('/') + .set('X-Forwarded-Proto', 'https') + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, done) + }) + }) + }) + + describe('combined with secure auto', function() { + before(function () { + function setup (req) { + req.secure = JSON.parse(req.headers['x-secure']) + } + + function respond (req, res) { + res.end(String(req.secure)) + } + + this.server = createServer(setup, { cookie: { secure: 'auto', sameSite: 'auto' } }, respond) + }) + + it('should set both Secure and SameSite=None when secure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'true') + .expect(shouldSetCookieWithAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, 'true', done) + }) + + it('should set neither Secure nor SameSite=None when insecure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, 'false', done) + }) + }) + }) }) describe('genid option', function(){ From 69ade9bcf9c0051be16f04c42b707b0b13a23092 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Wed, 17 Dec 2025 18:41:40 -0500 Subject: [PATCH 2/6] test: enhance session tests for secure and SameSite cookie attributes --- package.json | 2 +- test/session.js | 110 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 42d244b9..0f7d92e7 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ }, "scripts": { "lint": "eslint . && node ./scripts/lint-readme.js", - "test": "./test/support/gencert.sh && mocha --require test/support/env --check-leaks --bail --no-exit --reporter spec test/", + "test": "./test/support/gencert.sh && mocha --require test/support/env --check-leaks --no-exit --reporter spec test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc npm test", "version": "node scripts/version-history.js && git add HISTORY.md" diff --git a/test/session.js b/test/session.js index ac45d88a..651f2659 100644 --- a/test/session.js +++ b/test/session.js @@ -872,34 +872,100 @@ describe('session()', function(){ }) describe('combined with secure auto', function() { - before(function () { - function setup (req) { - req.secure = JSON.parse(req.headers['x-secure']) - } + describe('when "secure" is "auto"', function () { + before(function () { + function setup (req) { + req.secure = JSON.parse(req.headers['x-secure']) + } - function respond (req, res) { - res.end(String(req.secure)) - } + function respond (req, res) { + res.end(String(req.secure)) + } + + this.server = createServer(setup, { cookie: { secure: 'auto', sameSite: 'auto' } }, respond) + }) + + it('should set both Secure and SameSite=None when secure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'true') + .expect(shouldSetCookieWithAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, 'true', done) + }) - this.server = createServer(setup, { cookie: { secure: 'auto', sameSite: 'auto' } }, respond) + it('should set neither Secure nor SameSite=None when insecure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, 'false', done) + }) }) - it('should set both Secure and SameSite=None when secure', function (done) { - request(this.server) - .get('/') - .set('X-Secure', 'true') - .expect(shouldSetCookieWithAttribute('connect.sid', 'Secure')) - .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) - .expect(200, 'true', done) + describe('when "secure" is "false"', function () { + before(function () { + function setup (req) { + req.secure = JSON.parse(req.headers['x-secure']) + } + + function respond (req, res) { + res.end(String(req.secure)) + } + + this.server = createServer(setup, { cookie: { secure: false, sameSite: 'auto' } }, respond) + }) + + it('should set both Secure and SameSite=None when secure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'true') + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, 'true', done) + }) + + it('should set neither Secure nor SameSite=None when insecure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, 'false', done) + }) }) - it('should set neither Secure nor SameSite=None when insecure', function (done) { - request(this.server) - .get('/') - .set('X-Secure', 'false') - .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) - .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) - .expect(200, 'false', done) + describe('when "secure" is "true"', function () { + before(function () { + function setup (req) { + req.secure = JSON.parse(req.headers['x-secure']) + } + + function respond (req, res) { + res.end(String(req.secure)) + } + + this.server = createServer(setup, { cookie: { secure: true, sameSite: 'auto' } }, respond) + }) + + it('should set both Secure and SameSite=None when secure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'true') + .expect(shouldSetCookieWithAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'None')) + .expect(200, 'true', done) + }) + + it.only('should set neither Secure nor SameSite=None when insecure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(200, 'false', done) + }) }) }) }) From f6aa9ed036b49b1086cfb405cbb08a31ca908e85 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Wed, 17 Dec 2025 19:05:16 -0500 Subject: [PATCH 3/6] test: update session tests for SameSite cookie handling and logging --- test/session.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/session.js b/test/session.js index 651f2659..31f4707b 100644 --- a/test/session.js +++ b/test/session.js @@ -917,7 +917,7 @@ describe('session()', function(){ this.server = createServer(setup, { cookie: { secure: false, sameSite: 'auto' } }, respond) }) - it('should set both Secure and SameSite=None when secure', function (done) { + it('should set SameSite=None without Secure when secure', function (done) { request(this.server) .get('/') .set('X-Secure', 'true') @@ -926,7 +926,7 @@ describe('session()', function(){ .expect(200, 'true', done) }) - it('should set neither Secure nor SameSite=None when insecure', function (done) { + it('should set SameSite=Lax without Secure when insecure', function (done) { request(this.server) .get('/') .set('X-Secure', 'false') @@ -958,12 +958,11 @@ describe('session()', function(){ .expect(200, 'true', done) }) - it.only('should set neither Secure nor SameSite=None when insecure', function (done) { + it('should not set cookie when insecure', function (done) { request(this.server) .get('/') .set('X-Secure', 'false') - .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) - .expect(shouldSetCookieWithAttributeAndValue('connect.sid', 'SameSite', 'Lax')) + .expect(shouldNotHaveHeader('Set-Cookie')) .expect(200, 'false', done) }) }) From a19f25b6272dc9f92c9bc9b3393da3bc87f8d3d0 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Wed, 17 Dec 2025 19:12:04 -0500 Subject: [PATCH 4/6] docs: add history entry --- HISTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index e12d0a00..60cf6db4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,10 @@ ### 🚀 Improvements +* Add sameSite 'auto' support for automatic SameSite attribute configuration + + Added `sameSite: 'auto'` option for cookie configuration that automatically sets `SameSite=None` for HTTPS and `SameSite=Lax` for HTTP connections, simplifying cookie handling across different environments. + * deps: use tilde notation for dependencies 1.18.2 / 2025-07-17 From 6df617c1ab43b1e22686ec86ef4ef12e524b9efd Mon Sep 17 00:00:00 2001 From: djunehor Date: Wed, 24 Dec 2025 00:33:59 +0000 Subject: [PATCH 5/6] refactor: cache issecure result to avoid duplicate calls --- index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 022a94fd..43f20e78 100644 --- a/index.js +++ b/index.js @@ -160,12 +160,14 @@ function session(options) { req.session = new Session(req); req.session.cookie = new Cookie(cookieOptions); + const isSecure = issecure(req, trustProxy); + if (cookieOptions.secure === 'auto') { - req.session.cookie.secure = issecure(req, trustProxy); + req.session.cookie.secure = isSecure; } if (cookieOptions.sameSite === 'auto') { - req.session.cookie.sameSite = issecure(req, trustProxy) ? 'none' : 'lax'; + req.session.cookie.sameSite = isSecure ? 'none' : 'lax'; } }; From dbaf1ca96a6464cd45d614b42fa548b1e1eb3f21 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sat, 17 Jan 2026 17:09:16 -0500 Subject: [PATCH 6/6] Apply suggestion from @bjohansebas --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 43f20e78..1ee99eb5 100644 --- a/index.js +++ b/index.js @@ -160,7 +160,7 @@ function session(options) { req.session = new Session(req); req.session.cookie = new Cookie(cookieOptions); - const isSecure = issecure(req, trustProxy); + var isSecure = issecure(req, trustProxy); if (cookieOptions.secure === 'auto') { req.session.cookie.secure = isSecure;