Skip to content

fix(session): persist production sessions with Mongo-backed session store#552

Merged
mehul-m-prajapati merged 1 commit into
GitMetricsLab:mainfrom
Ridanshi:fix/persistent-session-store
May 31, 2026
Merged

fix(session): persist production sessions with Mongo-backed session store#552
mehul-m-prajapati merged 1 commit into
GitMetricsLab:mainfrom
Ridanshi:fix/persistent-session-store

Conversation

@Ridanshi
Copy link
Copy Markdown
Contributor

@Ridanshi Ridanshi commented May 26, 2026

Closes #453

Summary

Replaces the default express-session MemoryStore fallback with a persistent Mongo-backed session store using connect-mongo.

The backend previously initialized express-session without a configured store, causing Express to silently fall back to the built-in in-memory MemoryStore, which is explicitly not production-safe.

This PR introduces durable Mongo-backed session persistence while preserving the existing session/auth behavior.


Problems Fixed

The previous implementation caused:

  • sessions to be lost on restart/deploy/crash
  • memory growth under sustained traffic
  • incompatibility with multi-instance deployments
  • unstable authenticated user experience
  • inability to scale safely behind load balancers

Because all sessions existed only in process memory.


Implementation

Added shared session configuration using:

MongoStore.create({
  mongoUrl: process.env.MONGO_URI,
  ttl: 14 * 24 * 60 * 60,
})

Key changes:

  • replaced implicit MemoryStore fallback
  • configured persistent Mongo-backed sessions
  • added session TTL cleanup
  • enabled production secure-cookie handling
  • enabled trusted proxy handling for production deployments
  • added fail-fast validation for missing MONGO_URI
  • documented required session-related environment variables

Existing behavior preserved:

  • resave: false
  • saveUninitialized: false
  • existing auth/session architecture
  • existing session semantics

Files Changed

  • backend/config/session.js
  • backend/server.js
  • backend/package.json
  • package.json
  • spec/session.config.spec.cjs
  • README.md

Tests

Focused regression coverage added for:

  • MongoStore initialization
  • session TTL configuration
  • production secure-cookie behavior
  • no fallback to MemoryStore
  • session persistence configuration correctness

Verification

npx.cmd jasmine spec/session.config.spec.cjs

Result:

5 specs, 0 failures

Full backend suite was also attempted:

npm.cmd run test:backend

Existing unrelated MongoDB-dependent auth/model tests timed out locally, but all newly-added session persistence tests passed successfully.


Notes

This PR intentionally keeps the scope limited to production session persistence and infrastructure hardening.

No JWT migration, auth architecture rewrite, or unrelated middleware refactors were introduced.

Summary by CodeRabbit

  • Documentation

    • Added Backend Environment Variables section to guide local backend setup.
  • Improvements

    • Enhanced session management with centralized configuration and MongoDB persistence.
  • Tests

    • Added comprehensive test suite for session configuration and persistence.

Review Change Stack

@netlify
Copy link
Copy Markdown

netlify Bot commented May 26, 2026

Deploy Preview for github-spy ready!

Name Link
🔨 Latest commit 6fd37e9
🔍 Latest deploy log https://app.netlify.com/projects/github-spy/deploys/6a15f3c64f15ed00084ee78b
😎 Deploy Preview https://deploy-preview-552--github-spy.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

The PR centralizes session configuration into a dedicated module that wires express-session to MongoDB via connect-mongo, replacing the non-persistent default MemoryStore. The backend server now imports and uses this factory; both package manifests declare the connect-mongo dependency; comprehensive tests validate persistence, security, and error handling; and the README documents required environment variables.

Changes

MongoDB-Backed Session Store

Layer / File(s) Summary
Session Configuration Module
backend/config/session.js
Exports SESSION_TTL_SECONDS (14 days) and createSessionConfig factory that validates mongoUrl, constructs a connect-mongo store, configures session options with resave: false and saveUninitialized: false, and enables cookie.secure only in production. Accepts injectable storeFactory for testing.
Server Integration and Trust Proxy
backend/server.js
Imports createSessionConfig and mounts express-session with MongoDB backing. Adds conditional trust proxy set to 1 when NODE_ENV is production.
Dependency Declarations
backend/package.json, package.json
Both manifest files add connect-mongo ^5.1.0 to dependencies.
Session Configuration Tests
spec/session.config.spec.cjs
Defines in-memory TestSessionStore and factory helper. Tests validate: mongoUrl and TTL wiring to store, absence of MemoryStore fallback, production-only secure cookies, required configuration validation errors, and end-to-end session persistence via supertest request agent.
Environment Variables Documentation
README.md
Documents backend/.env with required MONGO_URI and SESSION_SECRET variables and optional NODE_ENV, describing their purposes for MongoDB connection and session security.

Sequence Diagram

sequenceDiagram
    participant StartupEnv as Startup Environment
    participant Server as backend/server.js
    participant ConfigFactory as createSessionConfig
    participant MongoStore as connect-mongo Store
    participant MongoDB as MongoDB Instance
    participant UserRequest as User Request
    participant SessionMW as express-session Middleware
    
    StartupEnv->>Server: MONGO_URI, SESSION_SECRET, NODE_ENV
    Server->>ConfigFactory: Call createSessionConfig()
    ConfigFactory->>MongoStore: new MongoStore({ mongoUrl, ttl })
    ConfigFactory-->>Server: SessionOptions with MongoDB store
    Server->>SessionMW: Mount session(createSessionConfig())
    
    UserRequest->>SessionMW: Incoming request
    SessionMW->>MongoStore: Check/retrieve session
    MongoStore->>MongoDB: Query session data
    MongoDB-->>MongoStore: Session doc
    MongoStore-->>SessionMW: Session data
    SessionMW-->>UserRequest: Request continues
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • GitMetricsLab/github_tracker#467: Hardens session cookie options including production-gated secure behavior, overlapping with this PR's session middleware refactor.

Suggested labels

level:intermediate, quality:clean

Poem

🐰 Hops through the memory leak,
Sessions now persist for weeks,
MongoDB keeps them safe and sound,
No restarts will knock users down! 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely and accurately describes the main change: replacing the implicit MemoryStore with a Mongo-backed session store for production persistence.
Description check ✅ Passed The PR description is comprehensive and well-structured. It includes the issue closure, clear summary, problems fixed, implementation details, files changed, tests, and notes; following the template with substantive content throughout.
Linked Issues check ✅ Passed All key objectives from #453 are met: MemoryStore replaced with MongoDB-backed sessions, persistent storage across restarts, multi-instance deployment support, TTL cleanup, secure cookies/trusted proxy for production, MONGO_URI validation, documentation, and regression tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to persistent session implementation and production hardening. No JWT migrations, auth rewrites, or unrelated middleware refactors were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
spec/session.config.spec.cjs (1)

89-95: ⚡ Quick win

Add a regression test for missing sessionSecret.

You already guard missing mongoUrl; add the same for sessionSecret so the fail-fast contract stays enforced.

Suggested test addition
   it('requires MongoDB configuration for persistent sessions', () => {
     expect(() => createSessionConfig({
       mongoUrl: '',
       sessionSecret: 'test-secret',
       storeFactory: createStoreFactory(),
     })).toThrowError(/MONGO_URI/);
   });
+
+  it('requires SESSION_SECRET for session middleware', () => {
+    expect(() => createSessionConfig({
+      mongoUrl: 'mongodb://127.0.0.1:27017/github_tracker_test',
+      sessionSecret: '',
+      storeFactory: createStoreFactory(),
+    })).toThrowError(/SESSION_SECRET/);
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/session.config.spec.cjs` around lines 89 - 95, Add a regression test
that asserts createSessionConfig throws when sessionSecret is empty, mirroring
the existing mongoUrl test: call createSessionConfig with mongoUrl set to a
valid value (or left as in other tests), sessionSecret: '' and storeFactory:
createStoreFactory(), and expect it toThrowError(/SESSION_SECRET|sessionSecret/)
to enforce the fail-fast contract for missing session secrets; place the test
next to the existing "requires MongoDB configuration for persistent sessions"
test and reuse the same patterns/helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/config/session.js`:
- Around line 11-17: Validate that sessionSecret is present and throw a clear
error before returning the express-session config: add a check for sessionSecret
(the value derived from SESSION_SECRET) similar to the mongoUrl check and throw
new Error('SESSION_SECRET is required to configure the session store') if
missing so you fail fast before express-session is configured; keep this
validation alongside the existing mongoUrl check and prior to returning the
object containing secret: sessionSecret.
- Around line 23-25: The session cookie config currently sets only secure;
update the cookie options in the session configuration (the cookie object) to
include sameSite: 'none' when nodeEnv === 'production' so cross-site
credentialed requests work; keep secure: nodeEnv === 'production' and for
non-production environments leave sameSite unset or set to a more restrictive
value (e.g., 'lax') as appropriate. Ensure this change is applied to the cookie
object used by your session middleware (the cookie property in the session
config).

---

Nitpick comments:
In `@spec/session.config.spec.cjs`:
- Around line 89-95: Add a regression test that asserts createSessionConfig
throws when sessionSecret is empty, mirroring the existing mongoUrl test: call
createSessionConfig with mongoUrl set to a valid value (or left as in other
tests), sessionSecret: '' and storeFactory: createStoreFactory(), and expect it
toThrowError(/SESSION_SECRET|sessionSecret/) to enforce the fail-fast contract
for missing session secrets; place the test next to the existing "requires
MongoDB configuration for persistent sessions" test and reuse the same
patterns/helpers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 213a17fe-5ec2-4504-a0af-fa73f20de9ab

📥 Commits

Reviewing files that changed from the base of the PR and between 4ae0ef6 and 6fd37e9.

📒 Files selected for processing (6)
  • README.md
  • backend/config/session.js
  • backend/package.json
  • backend/server.js
  • package.json
  • spec/session.config.spec.cjs

Comment thread backend/config/session.js
Comment on lines +11 to +17
if (!mongoUrl) {
throw new Error('MONGO_URI is required to configure the session store');
}

return {
secret: sessionSecret,
resave: false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In express-session v1.18.x, is the secret option required, and what happens when it is omitted?

💡 Result:

In express-session v1.18.x, the secret option is a required configuration [1][2][3]. It is used to sign the session ID cookie, which helps prevent tampering [3][4]. If the secret option is omitted, the middleware will fail to initialize properly. Specifically, the library explicitly checks for the presence of a secret during request handling and will trigger an error if none is found [5]. When a request is made, if no secret is provided (and no fallback is available), the middleware will call next with an error: "secret option required for sessions" [5]. Previous versions of the library may have allowed a fallback to req.secret (often provided by cookie-parser), but this practice is deprecated and will result in a warning; relying on it is not supported [6][7][5].

Citations:


Fail fast if SESSION_SECRET is missing before configuring express-session.

express-session requires the secret option; passing an undefined sessionSecret will cause the middleware to error when handling requests (instead of clear config validation in this module).

Proposed fix
 function createSessionConfig({
   mongoUrl = process.env.MONGO_URI,
   sessionSecret = process.env.SESSION_SECRET,
   nodeEnv = process.env.NODE_ENV,
   storeFactory = MongoStore,
 } = {}) {
   if (!mongoUrl) {
     throw new Error('MONGO_URI is required to configure the session store');
   }
+  if (!sessionSecret) {
+    throw new Error('SESSION_SECRET is required to configure session middleware');
+  }

   return {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/config/session.js` around lines 11 - 17, Validate that sessionSecret
is present and throw a clear error before returning the express-session config:
add a check for sessionSecret (the value derived from SESSION_SECRET) similar to
the mongoUrl check and throw new Error('SESSION_SECRET is required to configure
the session store') if missing so you fail fast before express-session is
configured; keep this validation alongside the existing mongoUrl check and prior
to returning the object containing secret: sessionSecret.

Comment thread backend/config/session.js
Comment on lines +23 to +25
cookie: {
secure: nodeEnv === 'production',
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For modern browsers, what SameSite value is required for cookies to be sent on cross-site fetch/XHR requests with credentials?

💡 Result:

For modern browsers, to ensure that cookies are sent on cross-site fetch or XHR requests (typically initiated via APIs like fetch or XMLHttpRequest), the cookie must be set with the SameSite=None attribute [1][2][3]. Additionally, when SameSite=None is used, the following requirements must be met: 1. The Secure attribute must also be included, meaning the cookie will only be transmitted over encrypted (HTTPS) connections [1][4][3]. 2. For the request itself, the client must explicitly opt-in to sending credentials. For fetch, this is done by setting the credentials option to 'include' [5][4][6]. For XMLHttpRequest, the withCredentials property must be set to true [4]. 3. The server must also correctly configure Cross-Origin Resource Sharing (CORS) headers, specifically setting Access-Control-Allow-Credentials to true [5][4]. Without setting SameSite=None, modern browsers will typically default to Lax, which restricts cookies to same-site contexts and top-level navigations, excluding them from cross-site subresource requests like fetch/XHR [1][7][2].

Citations:


Set sameSite for production session cookies when using cross-site credentialed requests.

secure alone isn’t sufficient for modern browsers to send cookies on cross-site fetch/XHR with credentials; that requires SameSite=None (and Secure). Add sameSite: 'none' in production (and keep secure enabled there).

Proposed fix
     cookie: {
       secure: nodeEnv === 'production',
+      sameSite: nodeEnv === 'production' ? 'none' : 'lax',
+      httpOnly: true,
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cookie: {
secure: nodeEnv === 'production',
},
cookie: {
secure: nodeEnv === 'production',
sameSite: nodeEnv === 'production' ? 'none' : 'lax',
httpOnly: true,
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/config/session.js` around lines 23 - 25, The session cookie config
currently sets only secure; update the cookie options in the session
configuration (the cookie object) to include sameSite: 'none' when nodeEnv ===
'production' so cross-site credentialed requests work; keep secure: nodeEnv ===
'production' and for non-production environments leave sameSite unset or set to
a more restrictive value (e.g., 'lax') as appropriate. Ensure this change is
applied to the cookie object used by your session middleware (the cookie
property in the session config).

@mehul-m-prajapati mehul-m-prajapati merged commit 101f5cc into GitMetricsLab:main May 31, 2026
9 checks passed
@github-actions
Copy link
Copy Markdown

🎉🎉 Thank you for your contribution! Your PR #552 has been merged! 🎉🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Bug Report: Default express-session MemoryStore Causes Memory Exhaustion and Total Session Loss on Restart

2 participants