最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - Express session loses passport user ID on a Safari cookie every week - Stack Overflow

programmeradmin3浏览0评论

I've been studying this bug for a while. I have a NodeJS application with cookies when logging in, and the cookies are supposed to last one year. I check the application every day on Google Chrome, where it works fine, and on Safari, where I have to login again every week or so. Both browsers keep the cookies and I can check the database for them.

The Google Chrome cookie (obfuscated on purpose) is:

> db.sessions.find({_id: "Sfztzfn6jhucgI3r6o9p3bw2wt"})
[
  {
    _id: 'Sfztzfn6jhucgI3r6o9p3bw2wt',
    expires: ISODate('2026-02-08T08:09:10.166Z'),
    session: {
      cookie: {
        originalMaxAge: 31535999999,
        partitioned: null,
        priority: null,
        expires: ISODate('2026-02-08T08:09:10.166Z'),
        secure: null,
        httpOnly: true,
        domain: null,
        path: '/',
        sameSite: 'lax'
      },
      passport: { user: 'Cw2mwjwn2my9sOhtldwgah4e4a' },
      flash: {}
    }
  }
]

The Safari cookie in the database is similar, but it is missing the passport field, and I believe this is why I need to login again on Safari:

> db.sessions.find({_id: "3NiGSxZSvoF354waptnhswaK7wmgvdqnbgzw"})
[
  {
    _id: '3NiGSxZSvoF354waptnhswaK7wmgvdqnbgzw',
    expires: ISODate('2026-02-15T07:34:28.833Z'),
    session: {
      cookie: {
        originalMaxAge: 31536000000,
        partitioned: null,
        priority: null,
        expires: ISODate('2026-02-15T07:34:28.833Z'),
        secure: null,
        httpOnly: true,
        domain: null,
        path: '/',
        sameSite: 'lax'
      },
      flash: {}
    }
  }
]

Here is the cookie on the Safari inspection blade, which has the same expiration date:

The NodeJS code to save cookies is:

const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);

const max_session_ms = 365 * 24 * 60 * 60 * 1000;

// Initialize mongodb session storage to remember users.
const store = new MongoDBStore({
  uri: MONGO_URI,
  expires: max_session_ms,
});


// Enable sessions using encrypted cookies
app.use(
  session({
    cookie: {
      maxAge: max_session_ms,
      sameSite: "lax",
    },
    store: store,
    secret: "some secrete",
    signed: true,
    resave: false,  // Unknown effect. See 
    saveUninitialized: false,  // Save only explicitly, e.g. when logging in.
    httpOnly: true,  // Don't let browser javascript access cookies.
    secure: true, // Use cookies over https in production.
  })
);

If I login on Safari again, then I can access the web app for around 7 days, until the session in the database loses the passport field again. This only happens for Safari, not for Google Chrome, and has been happening for several months.

How can I investigate and fix this bug?

I've been studying this bug for a while. I have a NodeJS application with cookies when logging in, and the cookies are supposed to last one year. I check the application every day on Google Chrome, where it works fine, and on Safari, where I have to login again every week or so. Both browsers keep the cookies and I can check the database for them.

The Google Chrome cookie (obfuscated on purpose) is:

> db.sessions.find({_id: "Sfztzfn6jhucgI3r6o9p3bw2wt"})
[
  {
    _id: 'Sfztzfn6jhucgI3r6o9p3bw2wt',
    expires: ISODate('2026-02-08T08:09:10.166Z'),
    session: {
      cookie: {
        originalMaxAge: 31535999999,
        partitioned: null,
        priority: null,
        expires: ISODate('2026-02-08T08:09:10.166Z'),
        secure: null,
        httpOnly: true,
        domain: null,
        path: '/',
        sameSite: 'lax'
      },
      passport: { user: 'Cw2mwjwn2my9sOhtldwgah4e4a' },
      flash: {}
    }
  }
]

The Safari cookie in the database is similar, but it is missing the passport field, and I believe this is why I need to login again on Safari:

> db.sessions.find({_id: "3NiGSxZSvoF354waptnhswaK7wmgvdqnbgzw"})
[
  {
    _id: '3NiGSxZSvoF354waptnhswaK7wmgvdqnbgzw',
    expires: ISODate('2026-02-15T07:34:28.833Z'),
    session: {
      cookie: {
        originalMaxAge: 31536000000,
        partitioned: null,
        priority: null,
        expires: ISODate('2026-02-15T07:34:28.833Z'),
        secure: null,
        httpOnly: true,
        domain: null,
        path: '/',
        sameSite: 'lax'
      },
      flash: {}
    }
  }
]

Here is the cookie on the Safari inspection blade, which has the same expiration date:

The NodeJS code to save cookies is:

const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);

const max_session_ms = 365 * 24 * 60 * 60 * 1000;

// Initialize mongodb session storage to remember users.
const store = new MongoDBStore({
  uri: MONGO_URI,
  expires: max_session_ms,
});


// Enable sessions using encrypted cookies
app.use(
  session({
    cookie: {
      maxAge: max_session_ms,
      sameSite: "lax",
    },
    store: store,
    secret: "some secrete",
    signed: true,
    resave: false,  // Unknown effect. See https://github/expressjs/session#resave
    saveUninitialized: false,  // Save only explicitly, e.g. when logging in.
    httpOnly: true,  // Don't let browser javascript access cookies.
    secure: true, // Use cookies over https in production.
  })
);

If I login on Safari again, then I can access the web app for around 7 days, until the session in the database loses the passport field again. This only happens for Safari, not for Google Chrome, and has been happening for several months.

How can I investigate and fix this bug?

Share Improve this question asked Feb 16 at 11:16 emonigmaemonigma 4,4164 gold badges37 silver badges83 bronze badges 2
  • 1 Safari's Intelligent Tracking Prevention (ITP) limits third-party cookies to 7 day by default and 24 hours if the URL has query parameters. If your domain: null setting is actually what you use then you should try setting the domain to your website domain instead so that it's considered a first-party one. There is a good blog on this subject you might find useful stape.io/blog/… – jQueeny Commented Feb 16 at 17:51
  • This seems to work and I now see a domain in the MongoDB store. Can you please write an answer and I'll accept it? – emonigma Commented 23 hours ago
Add a comment  | 

1 Answer 1

Reset to default 0

Apple's Intelligent Tracking Prevention (ITP) used in Safari's browser engine Webkit, limits 3rd party cookies to 7 days by default and 24 hours if the URL has query parameters.

Given your domain: null option within the cookie, Safari probably sees that as a 3rd party cookie and is therefore limiting it to the 7 days.

Setting domain: mysite where mysite is the domain you intend for the cookie to be used, should tell Safari that it's a 1st party cookie.

Note 1: the cookie.domian property in express-session:

By default, no domain is set, and most clients will consider the cookie to apply to only the current domain.

So you could also try omitting the domain property altogether. However, the "most clients" is where Safari might not fit.

Note 2: Webkits 7-Day Cap on All Script-Writeable Storage rules mean that:

ITP deletes all cookies created in JavaScript and all other script-writeable storage after 7 days of no user interaction with the website. The latter storage forms are:

  • List item
  • IndexedDB
  • LocalStorage
  • Media keys
  • SessionStorage
  • Service Worker registrations and cache
发布评论

评论列表(0)

  1. 暂无评论