Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session created for every request #484

Open
oshihirii opened this issue Aug 10, 2024 · 1 comment
Open

Session created for every request #484

oshihirii opened this issue Aug 10, 2024 · 1 comment

Comments

@oshihirii
Copy link

  • I'm submitting a ...

  • [ X ] question about how to use this project

  • Summary

I've been troubleshooting this issue for several hours and have googled things like:

"express-session and connect-mongo create session on every request"

I can also see that several issues in this repo on the same topic have been resolved and closed.

I have tried all the solutions that others seem to have found resolved the issue, ie:

  • Set saveUninitialized to false
  • Set sameSite to none
  • Add cors and set credentials to true
  • Change the order of the middleware
  • (and a lot of 'random' other settings to see if anything worked)

My initial problem started when I noticed an Azure AD auto-signin redirect back to my application's /redirect endpoint was creating a different session than the one that was created during the initial Azure AD B2C signin, and so I could see that two session object entries were added to my MongoDB Atlas database.

Then I added some console.log() statements to log out req.session values and sessions started being created in the database for every request (images, js, css etc).

I just thought I would post a chunk of my testing code below to ask if I am doing anything obviously wrong:

// this returns a mongodb client 
const mongodb_client = await mongodb_client_manager.open();
console.log("mongodb client connection established");

// connects to the rate limiter store database 
const rate_limits_db = mongodb_client.db('rate_limits');
console.log("rate limiter db connection established");

// connects to the express-session store database
const express_sessions_db = mongodb_client.db('express_sessions');
console.log("express sessions db connection established");

// was playing around with these - they will only create the index if it doesn't already exist
// await rate_limits_db.collection('rate_limits').createIndex({ expires: 1 }, { expireAfterSeconds: 0 });
// await express_sessions_db.collection('express_sessions').createIndex({ expires: 1 }, { expireAfterSeconds: 0 });

dotenv.config();

const PORT = process.env.PORT || 3000;

const app = express();
const httpServer = createServer(app);

// CORS configuration
const cors_options = {
  // replace with your allowed origin(s) - currently just testing in local environment  
  origin: 'http://localhost:3000', 
  // allow credentials (cookies, authorization headers, etc.)
  credentials: true, 
};

app.use(cors(cors_options));

// session configuration object
const session_config = {
  // secret used to sign the session ID cookie
  secret: process.env.SESSION_SECRET,
  // forces the session to be saved back to the session store, even if it was never modified during the request
  resave: false,
  // forces a session that is "uninitialized" to be saved to the store
  saveUninitialized: false,
  // cookie settings
  cookie: {
    // secure cookie only sent over HTTPS  
    // returns true if we are online, false if we are not
    secure: process.env.NODE_ENV === "production",  
    // 2 hours in milliseconds, if not set, the cookie will expire when the browser is closed
    // maxAge: 2 * 60 * 60 * 1000 
    httpOnly: true,
    sameSite: 'none',
    path: '/', // i was just testing these values out of desperation...
    domain: 'localhost'
  },
  // use connect-mongo to store sessions in MongoDB
  store: MongoStore.create({
    // use the existing mongodb client
    clientPromise: Promise.resolve(mongodb_client),   // it didn't like the client itself, so i wrapped it in a Promise.resolve()  
    // specify the database name
    dbName: 'express_sessions', 
    // specify the collection name
    collectionName: 'express_sessions', 
    // native is the default
    autoRemove: 'native', 
    // crypto: {
    //   secret: process.env.SESSION_STORAGE_CRYPTO_SECRET,
    //   algorithm: 'aes-256-gcm'
    // }
  })
};
app.use(session(session_config));

// global rate limiter - still testing  
const global_rate_limiter = rate_limit({
  // request window of 15 minutes
  windowMs: 15 * 60 * 1000, 
  // limit each IP to 1000 requests per request window 
  max: 1000, 
  store: new custom_rate_limiter_mongodb_store(rate_limits_db),
  keyGenerator: (req, res) => `global:${req.ip}`
});

// apply global rate limiter to all requests
app.use(global_rate_limiter);

app.use(
  helmet({
    crossOriginEmbedderPolicy: false,
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "https://cdnjs.cloudflare.com"],
        connectSrc: ["'self'"],
        styleSrc: [
          "'self'",
          "https://cdnjs.cloudflare.com",
          "https://fonts.googleapis.com",
        ],
        fontSrc: ["'self'", "https://fonts.gstatic.com"],
        imgSrc: ["'self'", "data:"],
        frameSrc: ["'self'"],
      },
    },
  })
);

app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static("dist"));

app.use("/", routes);

I'd appreciate any tips that could lead to a solution.

Thank You.

@oshihirii
Copy link
Author

I think I might have found a solution, atleast when testing in my local environment:

  • I needed to install cookie-parser and import it:
import cookieParser from 'cookie-parser';
  • I needed to add it above the session middleware

  • I needed to remove the sameSite and domain properties and set secure to false:

// CORS configuration
const cors_options = {
  origin: 'http://localhost:3000', // Replace with your allowed origin(s)
  credentials: true, // Allow credentials (cookies, authorization headers, etc.)
};

app.use(cors(cors_options));

app.use(cookieParser()); 

// session configuration object
const session_config = {
  // secret used to sign the session ID cookie
  secret: process.env.SESSION_SECRET,
  // forces the session to be saved back to the session store, even if it was never modified during the request
  resave: false,
  // forces a session that is "uninitialized" to be saved to the store
  saveUninitialized: false,
  // cookie settings
  cookie: {
    // secure cookie only sent over HTTPS if in production
    secure: false,  
    // maxAge: 2 * 60 * 60 * 1000 // 2 hours in milliseconds, if not set, the cookie will expire when the browser is closed
    httpOnly: true,
    path: '/'
  },
  // use connect-mongo to store sessions in MongoDB
  store: MongoStore.create({
    clientPromise: Promise.resolve(mongodb_client), // use the existing mongodb client
    dbName: 'express_sessions', // specify the database name
    collectionName: 'express_sessions', // specify the collection name
    autoRemove: 'native', // native is the default
    // crypto: {
    //   secret: process.env.SESSION_STORAGE_CRYPTO_SECRET,
    //   algorithm: 'aes-256-gcm'
    // }
  })
};
app.use(session(session_config));

I am still testing, but wanted to post back in case it saved someone the time in writing a response.

Obviously, I will need to modify the security related settings for production, but I will look at that next.

The good news is now only 1 session is being created in the MongoDB database.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant