From 1aa756d20a1af2b922e9897ca0010fa466ce17e7 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Wed, 23 Jul 2025 07:42:19 -0500 Subject: [PATCH 01/14] chore: add funding to package.json (#1071) --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 7039d1d4..3bc6ac59 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,10 @@ "Joe Wagner " ], "repository": "expressjs/session", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + }, "license": "MIT", "dependencies": { "cookie": "0.7.2", From 488102d3d79ee394c90e0b051bf0dbe137956400 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 09:29:37 +0100 Subject: [PATCH 02/14] build(deps): bump actions/download-artifact from 4.3.0 to 6.0.0 (#1086) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 210657e2..167e2e38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -229,7 +229,7 @@ jobs: run: sudo apt-get -y install lcov - name: Collect coverage reports - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: ./coverage From b6f79be32f4afd88b92eda9a3448b7136cecac1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Nov 2025 09:30:20 +0100 Subject: [PATCH 03/14] build(deps): bump github/codeql-action from 3.28.18 to 4.31.2 (#1085) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1a41f87b..bdff87f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -38,7 +38,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: languages: javascript # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,6 +61,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: category: "/language:javascript" \ No newline at end of file diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d53e5871..a6a29448 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,6 +68,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 + uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: sarif_file: results.sarif \ No newline at end of file From c83c8e28d586c3f86f9844356590b2e704843f57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:15:55 +0100 Subject: [PATCH 04/14] build(deps): bump coverallsapp/github-action from 2.3.6 to 2.3.7 (#1091) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 167e2e38..f7078328 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -238,6 +238,6 @@ jobs: run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./coverage/lcov.info - name: Upload coverage report - uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master + uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # master with: github-token: ${{ secrets.GITHUB_TOKEN }} From a095a9a17c862746af8c2d8671825e4743d38169 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:16:45 +0100 Subject: [PATCH 05/14] build(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 (#1090) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7078328..30a606f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -208,7 +208,7 @@ jobs: fi - name: Upload code coverage - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: steps.list_env.outputs.nyc != '' with: name: coverage-${{ matrix.node-version }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index a6a29448..a6efdc9e 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,7 +60,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: SARIF file path: results.sarif From 0e7a438512fa584c617d681cf7fca8fb5c42da5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:17:30 +0100 Subject: [PATCH 06/14] build(deps): bump github/codeql-action from 4.31.2 to 4.31.6 (#1089) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index bdff87f4..31ffb64a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -38,7 +38,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 with: languages: javascript # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,6 +61,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 with: category: "/language:javascript" \ No newline at end of file diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index a6efdc9e..51230d44 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,6 +68,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v4.31.6 with: sarif_file: results.sarif \ No newline at end of file From 1307f30a62d10cf33f7abd59b8ecf3aae428ff3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 09:18:56 +0100 Subject: [PATCH 07/14] build(deps): bump actions/checkout from 4.2.2 to 6.0.0 (#1088) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- .github/workflows/codeql.yml | 2 +- .github/workflows/scorecard.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30a606f2..13336155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,7 +130,7 @@ jobs: node-version: "21.6" steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Node.js ${{ matrix.node-version }} shell: bash -eo pipefail -l {0} @@ -222,7 +222,7 @@ jobs: needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install lcov shell: bash diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 31ffb64a..8534ee51 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 51230d44..30f54d2b 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false From 2cd6561f64cb9e0b847237885802351606d4029d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:04:48 -0500 Subject: [PATCH 08/14] build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.3 (#1082) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.1 to 2.4.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/f49aabe0b5af0936a0987cfb85d86b75731b0186...4eaacf0543bb3f2c246792bd56e8cdeffafb205a) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 30f54d2b..169262af 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -38,7 +38,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif From 00b8a5ff111bba1a0ca9bb3d84947446c198a9a3 Mon Sep 17 00:00:00 2001 From: Ayoub Mabrouk <77799760+Ayoub-Mabrouk@users.noreply.github.com> Date: Thu, 18 Dec 2025 02:53:00 +0100 Subject: [PATCH 09/14] refactor: remove unused `sess` parameter from `generateSessionId` function (#1001) The `sess` parameter was not used in `generateSessionId`, so it was removed to improve code clarity and reduce potential confusion. Co-authored-by: Sebastian Beltran --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d41b2378..85a0330c 100644 --- a/index.js +++ b/index.js @@ -523,7 +523,7 @@ function session(options) { * @private */ -function generateSessionId(sess) { +function generateSessionId() { return uid(24); } From 6d69f090d6239394e008cf39a2be2d5f60bdde97 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 18 Dec 2025 06:45:22 -0500 Subject: [PATCH 10/14] chore: remove history.md from being packaged on publish (#1097) --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 3bc6ac59..6d04c9c1 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ }, "files": [ "session/", - "HISTORY.md", "index.js" ], "engines": { From 264b6a0360a1d4b34955c27948bf1b1cfad74d66 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 18 Dec 2025 06:46:00 -0500 Subject: [PATCH 11/14] deps: use tilde notation for dependencies (#1096) --- HISTORY.md | 6 ++++++ package.json | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 9aacf124..e12d0a00 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,9 @@ +# Unreleased changes + +### ๐Ÿš€ Improvements + +* deps: use tilde notation for dependencies + 1.18.2 / 2025-07-17 ========== * deps: mocha@10.8.2 diff --git a/package.json b/package.json index 6d04c9c1..42d244b9 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,13 @@ }, "license": "MIT", "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", + "cookie": "~0.7.2", + "cookie-signature": "~1.0.7", + "debug": "~2.6.9", "depd": "~2.0.0", "on-headers": "~1.1.0", "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", + "safe-buffer": "~5.2.1", "uid-safe": "~2.1.5" }, "devDependencies": { From 73e0193afbee24bca8744e4fc8a67b92810301e8 Mon Sep 17 00:00:00 2001 From: Zacchaeus Bolaji Date: Sat, 17 Jan 2026 23:20:37 +0100 Subject: [PATCH 12/14] Add sameSite 'auto' support to match secure 'auto' pattern (#1087) * feat: add sameSite 'auto' support for automatic cross-site cookie configuration * test: enhance session tests for secure and SameSite cookie attributes * test: update session tests for SameSite cookie handling and logging * docs: add history entry * refactor: cache issecure result to avoid duplicate calls * Apply suggestion from @bjohansebas --------- Co-authored-by: Sebastian Beltran --- HISTORY.md | 4 ++ README.md | 9 +++ index.js | 8 ++- package.json | 2 +- test/session.js | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 188 insertions(+), 2 deletions(-) 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 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..1ee99eb5 100644 --- a/index.js +++ b/index.js @@ -160,8 +160,14 @@ function session(options) { req.session = new Session(req); req.session.cookie = new Cookie(cookieOptions); + var 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 ? 'none' : 'lax'; } }; 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 a7d79b70..31f4707b 100644 --- a/test/session.js +++ b/test/session.js @@ -801,6 +801,173 @@ 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() { + 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)) + } + + 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('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 SameSite=None without Secure 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 SameSite=Lax without Secure 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('should not set cookie when insecure', function (done) { + request(this.server) + .get('/') + .set('X-Secure', 'false') + .expect(shouldNotHaveHeader('Set-Cookie')) + .expect(200, 'false', done) + }) + }) + }) + }) }) describe('genid option', function(){ From 2673736548304359d3d3fb8f8d3019b4ea5b61a7 Mon Sep 17 00:00:00 2001 From: Lincon Date: Mon, 19 Jan 2026 11:44:13 -0300 Subject: [PATCH 13/14] feat: add support to dynamic cookie options (#1027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastian Beltran Co-authored-by: Ulises Gascรณn --- HISTORY.md | 21 ++++++++++++++++++++ README.md | 20 +++++++++++++++++++ index.js | 7 ++++--- test/session.js | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 60cf6db4..e28c5427 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,27 @@ ### ๐Ÿš€ Improvements +* Add dynamic cookie options support + + Cookie options can now be dynamic, allowing for more flexible and context-aware configuration based on each request. This feature enables programmatic modification of cookie attributes like `secure`, `httpOnly`, `sameSite`, `maxAge`, `domain`, and `path` based on session or request conditions. + + ```js + var app = express() + app.use(session({ + secret: 'keyboard cat', + resave: false, + saveUninitialized: true, + cookie: function (req) { + var match = req.url.match(/^\/([^/]+)/); + return { + path: match ? '/' + match[1] : '/', + httpOnly: true, + secure: req.secure || false, + maxAge: 60000 + } + } + })) + ``` * 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. diff --git a/README.md b/README.md index 4f19ce59..292b2024 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,26 @@ For a list of stores, see [compatible session stores](#compatible-session-stores Settings object for the session ID cookie. The default value is `{ path: '/', httpOnly: true, secure: false, maxAge: null }`. +In addition to providing a static object, you can also pass a callback function to dynamically generate the cookie options for each request. The callback receives the `req` object as its argument and should return an object containing the cookie settings. + +```js +var app = express() +app.use(session({ + secret: 'keyboard cat', + resave: false, + saveUninitialized: true, + cookie: function(req) { + var match = req.url.match(/^\/([^/]+)/); + return { + path: match ? '/' + match[1] : '/', + httpOnly: true, + secure: req.secure || false, + maxAge: 60000 + } + } +})) +``` + The following are options that can be set in this object. ##### cookie.domain diff --git a/index.js b/index.js index 1ee99eb5..1a509318 100644 --- a/index.js +++ b/index.js @@ -70,7 +70,7 @@ var defer = typeof setImmediate === 'function' * Setup session store with the given `options`. * * @param {Object} [options] - * @param {Object} [options.cookie] Options for cookie + * @param {Object|Function} [options.cookie] Options for cookie * @param {Function} [options.genid] * @param {String} [options.name=connect.sid] Session ID cookie name * @param {Boolean} [options.proxy] @@ -158,7 +158,7 @@ function session(options) { store.generate = function(req){ req.sessionID = generateId(req); req.session = new Session(req); - req.session.cookie = new Cookie(cookieOptions); + req.session.cookie = new Cookie(typeof cookieOptions === 'function' ? cookieOptions(req) : cookieOptions); var isSecure = issecure(req, trustProxy); @@ -199,7 +199,8 @@ function session(options) { // pathname mismatch var originalPath = parseUrl.original(req).pathname || '/' - if (originalPath.indexOf(cookieOptions.path || '/') !== 0) { + var resolvedCookieOptions = typeof cookieOptions === 'function' ? cookieOptions(req) : cookieOptions + if (originalPath.indexOf(resolvedCookieOptions.path || '/') !== 0) { debug('pathname mismatch') next() return diff --git a/test/session.js b/test/session.js index 31f4707b..46fed763 100644 --- a/test/session.js +++ b/test/session.js @@ -802,6 +802,58 @@ describe('session()', function(){ }) }) + describe('when "cookie" is a function', function () { + it('should call custom function and apply cookie options', function (done) { + var cookieCallbackCalled = false; + var cookieCallback = function () { + cookieCallbackCalled = true; + return { path: '/test', httpOnly: true, secure: false }; + }; + var server = createServer({ cookie: cookieCallback }); + request(server) + .get('/test') + .expect( + shouldSetCookieWithAttributeAndValue('connect.sid', 'Path', '/test') + ) + .expect(shouldSetCookieWithAttribute('connect.sid', 'HttpOnly')) + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(200, function (err) { + if (err) return done(err); + assert.strictEqual( + cookieCallbackCalled, + true, + 'should have called cookie callback' + ); + done(); + }); + }); + + it('should provide req argument', function (done) { + var _path = '/test'; + var cookieCallbackCalled = false; + var cookieCallback = function (req) { + cookieCallbackCalled = true; + return { path: req.url, httpOnly: true, secure: false }; + }; + var server = createServer({ cookie: cookieCallback }); + request(server) + .get(_path) + .expect( + shouldSetCookieWithAttributeAndValue('connect.sid', 'Path', _path) + ) + .expect(shouldSetCookieWithAttribute('connect.sid', 'HttpOnly')) + .expect(shouldSetCookieWithoutAttribute('connect.sid', 'Secure')) + .expect(200, function (err) { + if (err) return done(err); + assert.strictEqual( + cookieCallbackCalled, + true, + 'should have called cookie callback' + ); + done(); + }); + }); + }); describe('when "sameSite" set to "auto"', function () { describe('basic functionality', function () { before(function () { From c10b2a383841766899f6dcf53c8aff8c7fd3d2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Thu, 22 Jan 2026 15:44:27 +0100 Subject: [PATCH 14/14] 1.19.0 (#1107) --- HISTORY.md | 36 ++++++------------------------------ package.json | 2 +- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e28c5427..fae1a0f1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,33 +1,9 @@ -# Unreleased changes - -### ๐Ÿš€ Improvements - -* Add dynamic cookie options support - - Cookie options can now be dynamic, allowing for more flexible and context-aware configuration based on each request. This feature enables programmatic modification of cookie attributes like `secure`, `httpOnly`, `sameSite`, `maxAge`, `domain`, and `path` based on session or request conditions. - - ```js - var app = express() - app.use(session({ - secret: 'keyboard cat', - resave: false, - saveUninitialized: true, - cookie: function (req) { - var match = req.url.match(/^\/([^/]+)/); - return { - path: match ? '/' + match[1] : '/', - httpOnly: true, - secure: req.secure || false, - maxAge: 60000 - } - } - })) - ``` -* 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.19.0 / 2026-01-22 +========== + + * Add dynamic cookie options support + * Add sameSite 'auto' support for automatic SameSite attribute configuration + * deps: use tilde notation for dependencies 1.18.2 / 2025-07-17 ========== diff --git a/package.json b/package.json index 0f7d92e7..36387796 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-session", - "version": "1.18.2", + "version": "1.19.0", "description": "Simple session middleware for Express", "author": "TJ Holowaychuk (http://tjholowaychuk.com)", "contributors": [