0

My express server is using express-session and connect-mongo and it's generating a new session for each request from the same user instead of persisting one session like it's supposed to. This is my first time using express sessions but I don't think it's a problem with my code. Whilst testing I'm the only user and when I look at my MongoDB there's a bunch of new sessions. Every time I make a request to a route that needs to use a session variable it generates another new session.

Edit: The session id is not being stored by the client, thus causing a new session with every request. The correct set-cookie header is being sent so I'm not sure why it's not working. I'm using Firefox as my client.

Edit 2: Tried with Edge and Chrome and they aren't setting the session cookie either. I'm new to sessions so I have no clue why it isn't working properly.

// 3rd party modules
const util = require('util');
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer');
const morgan = require('morgan');
const mongoose = require('mongoose');
const config = require('config');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);

// Custom classes
const AuthService = require('./services/authService');

// Models
const Account = require('./models/account');

// Load .env config file
const envConfigResult = require('dotenv').config();
if (envConfigResult.error) {
    throw envConfigResult.error;
}

// Instances
let auth;
let upload = multer();
const app = express();

// Parse request data and store in req.body
app.use(bodyParser.json()); // json
app.use(bodyParser.urlencoded({ extended: true })); // x-www-form-url-encoded
app.use(upload.array()); // multipart/form-data

// Setup logging
app.use(morgan('dev'));

// JWT error handling
app.use(function (err, req, res, next) {
    // TODO: Implement proper error code
    res.status(401).send(err);
    if (err.name === 'UnauthorizedError') {
        res.status(401).send('invalid token...');
    }
});

async function main() {
    try {
        const mongoDbUrl = process.env.DB_HOST;
        // Use new url parser and unified topology to fix deprecation warning
        const mongoConnection = mongoose.connect(mongoDbUrl, { useNewUrlParser: true, useUnifiedTopology: true }).then(async function() {
            console.log(`Successfully connected to MongoDB database at ${mongoDbUrl}!`);
        }).catch(function(error) {
            console.log(`Error whilst connecting to MongoDB database at ${mongoDbUrl}! ${error}`);
        });
        mongoose.set('useCreateIndex', true); // Fixes deprecation warning
    } catch (err) {
        console.log(`Error whilst doing database setup! ${err}`);
    }
    try {
        app.use(session({
            store: new MongoStore({ mongooseConnection: mongoose.connection, ttl: 86400 }),
            secret: process.env.SESSION_SECRET,
            resave: false,
            saveUninitialized: false,
            cookie: {
                path: "/",
                maxAge: 3600000, // 1 hour
                httpOnly: false,
                secure: false // TODO: Update this on production
            }
        }));
        console.log("Sessions successfully initialized!");
    } catch (err) {
        console.log(`Error setting up a mongo session store! ${err}`);
    }
    try {
        // TODO: Initialize email service
        auth = new AuthService.AuthService();
        // TODO: Attach email service to auth service
    } catch (err) {
        console.log(`Error whilst doing initial setup. ${err}`);
    }
}

main();

// Routes
const rootRoute = require('./routes/root');
const tokensRoute = require('./routes/tokens');
const captchaRoute = require('./routes/captcha');

// Route middlewares
app.use('/v1/', rootRoute.router);
app.use('/v1/tokens', tokensRoute.router);
app.use('/v1/captcha', captchaRoute.router);

try {
    rootRoute.setAuthService(auth);
    tokensRoute.setAuthService(auth);
} catch (err) {
    console.log(`Error attaching auth service to routes! ${err}`);
}

var server = app.listen(8088, function() {
    let host = server.address().address;
    let port = server.address().port;

    if (host == "::") {
        console.log("Server running at http://127.0.0.1:%d", port);
    } else {
        console.log("Server running at http://%s:%d", host, port);
    }
});
PlanetVaster
  • 507
  • 1
  • 6
  • 18
  • I think it's because your async function. I think you're not waiting for it before run exporting the app. Put a console log just one line before your `app.use(session...)` and one console log one line after `main()` . If the consoleLog after the main() is trigger before, you've a problem with the async func.... – BENARD Patrick Dec 17 '19 at 10:15

2 Answers2

0

As explained in the doc (or npm page), it seems that you forgot one line :

app.use(session({
    secret: 'foo',
    store: new MongoStore(options)
}));
BENARD Patrick
  • 30,363
  • 16
  • 99
  • 105
  • I have that code within my main function. Sessions do work so it is being initialized correctly, rather the problem is a user isn't getting a single unique persistent session. Instead every request from the user is being assigned a new session. – PlanetVaster Dec 16 '19 at 19:00
0

It turns out that modern browsers ignore set-cookie headers unless you include credentials: 'include' in your request. So I switched the client to using fetch instead of XMLHttpRequest and added credentials: 'include' to its options. (Credentials are ignored if you don't have CORS set up correctly, so I had to do that as well.) Now it works fine.

TLDR: Include credentials: 'include' in your request and make sure the server has CORS configured.

PlanetVaster
  • 507
  • 1
  • 6
  • 18