From 35186ca87fd3b5ebb7899e836e20880a8e286585 Mon Sep 17 00:00:00 2001 From: Ulas Date: Mon, 13 Apr 2026 21:36:35 +0200 Subject: [PATCH] fix: change SameSite=Strict to SameSite=Lax for session and CSRF cookies (#46) Safari's ITP blocks Strict cookies on certain navigations (direct URL entry, reverse proxy context), resulting in a 401 on login even with valid credentials. Lax is safe: CSRF attacks are prevented by the double-submit token and the HTTPS-only secure flag. Firefox and Chrome were unaffected. --- CHANGELOG.md | 5 +++++ package.json | 2 +- server/auth.js | 7 +++++-- server/middleware/csrf.js | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ecea91..a0cf004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.17.2] - 2026-04-13 + +### Fixed +- Auth: session cookie and CSRF cookie changed from `SameSite=Strict` to `SameSite=Lax` - Safari's ITP (Intelligent Tracking Prevention) was blocking `Strict` cookies on certain navigations (direct URL entry, reverse proxy), causing a 401 on login while other browsers worked fine (#46) + ## [0.17.1] - 2026-04-13 ### Fixed diff --git a/package.json b/package.json index 64e75f7..fa4bb87 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.17.1", + "version": "0.17.2", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module", diff --git a/server/auth.js b/server/auth.js index 4a230d4..bf13a04 100644 --- a/server/auth.js +++ b/server/auth.js @@ -104,7 +104,10 @@ const sessionMiddleware = session({ httpOnly: true, // secure=true by default; set SESSION_SECURE=false in .env to allow HTTP (local dev without reverse proxy) secure: process.env.SESSION_SECURE !== 'false', - sameSite: 'strict', + // lax (not strict): Safari ITP blocks strict cookies on certain navigations + // (e.g. reverse proxy, direct URL entry), causing 401 on login. Lax is safe + // because CSRF is protected by the double-submit token and HTTPS secure flag. + sameSite: 'lax', maxAge: 1000 * 60 * 60 * 24 * 7, // 7 Tage in ms }, }); @@ -193,7 +196,7 @@ router.post('/login', loginLimiter, async (req, res) => { // CSRF-Token als Cookie setzen (nicht httpOnly → lesbar für JS) res.cookie('csrf-token', req.session.csrfToken, { httpOnly: false, - sameSite: 'strict', + sameSite: 'lax', secure: process.env.SESSION_SECURE !== 'false', maxAge: 1000 * 60 * 60 * 24 * 7, }); diff --git a/server/middleware/csrf.js b/server/middleware/csrf.js index f9a3995..68a003e 100644 --- a/server/middleware/csrf.js +++ b/server/middleware/csrf.js @@ -33,10 +33,10 @@ function csrfMiddleware(req, res, next) { req.session.csrfToken = generateToken(); } - // Cookie bei jedem Request erneuern (SameSite=Strict, nicht httpOnly → JS-lesbar) + // Cookie bei jedem Request erneuern (SameSite=Lax, nicht httpOnly → JS-lesbar) res.cookie('csrf-token', req.session.csrfToken, { httpOnly: false, - sameSite: 'strict', + sameSite: 'lax', secure: process.env.SESSION_SECURE !== 'false', maxAge: 1000 * 60 * 60 * 24 * 7, // 7 Tage (gleich wie Session) });