Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persistent login stopped working with Node.js, Express, PassportJS, Connect-Mongo

At some point persistent login with my application stopped working and I have no idea why. The thing is that even when I simply refresh the page, the app, the user is logged out. I built this application off of scaffolding provided by MEAN.js, so I'm having trouble locating the problem. Can I please have help debugging this? Any help appreciated.

Heres my express setup file

var fs = require('fs'),
    http = require('http'),
    https = require('https'),
    express = require('express'),
    morgan = require('morgan'),
    bodyParser = require('body-parser'),
    session = require('express-session'),
    compress = require('compression'),
    acl = require('acl'),
    methodOverride = require('method-override'),
    cookieParser = require('cookie-parser'),
    helmet = require('helmet'),
    passport = require('passport'),
    mongoStore = require('connect-mongo')({
        session: session
    }),
    flash = require('connect-flash'),
    config = require('./config'),
    consolidate = require('consolidate'),
    path = require('path');

module.exports = function(db) {
    // Initialize express app
    var app = express();

    // Globbing model files
    config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
        require(path.resolve(modelPath));
    });

    /**
     * Configure the modules ACL policies
     */
    // Globbing policy files
    config.getGlobbedFiles('app/policies/*.js').forEach(function(policyPath) {
        require(path.resolve(policyPath)).invokeRolesPolicies();
    });

    // Setting application local variables
    app.locals.title = config.app.title;
    app.locals.description = config.app.description;
    app.locals.keywords = config.app.keywords;
    app.locals.facebookAppId = config.facebook.clientID;
    app.locals.jsFiles = config.getJavaScriptAssets();
    app.locals.cssFiles = config.getCSSAssets();

    // Passing the request url to environment locals
    app.use(function(req, res, next) {
        res.locals.url = req.protocol + '://' + req.headers.host + req.url;
        next();
    });

    // Should be placed before express.static
    app.use(compress({
        filter: function(req, res) {
            return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
        },
        level: 9
    }));

    // Showing stack errors
    app.set('showStackError', true);

    // Set swig as the template engine
    app.engine('server.view.html', consolidate[config.templateEngine]);

    // Set views path and view engine
    app.set('view engine', 'server.view.html');
    app.set('views', './app/views');

    // Environment dependent middleware
    if (process.env.NODE_ENV === 'development') {
        // Enable logger (morgan)
        app.use(morgan('dev'));

        // Disable views cache
        app.set('view cache', false);
    } else if (process.env.NODE_ENV === 'production') {
        app.locals.cache = 'memory';
    }

    // Request body parsing middleware should be above methodOverride
    app.use(bodyParser.urlencoded({
        extended: true
    }));
    app.use(bodyParser.json());
    app.use(methodOverride());

    // CookieParser should be above session
    app.use(cookieParser());

    // Express MongoDB session storage
    app.use(session({
        saveUninitialized: true,
        resave: true,
        secret: config.sessionSecret,
        cookie: {
            maxAge: config.sessionCookie.maxAge,
            httpOnly: config.sessionCookie.httpOnly,
            secure: config.sessionCookie.secure && config.secure.ssl
        },
        key: config.sessionKey,
        store: new mongoStore({
            db: db.connection.db,
            collection: config.sessionCollection
        })
    }));

    // use passport session
    app.use(passport.initialize());
    app.use(passport.session());

    // connect flash for flash messages
    app.use(flash());

    // Use helmet to secure Express headers
    app.use(helmet.xframe());
    app.use(helmet.xssFilter());
    app.use(helmet.nosniff());
    app.use(helmet.ienoopen());
    app.disable('x-powered-by');

    // Setting the app router and static folder
    app.use(express.static(path.resolve('./public')));

    // Globbing routing files
    config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
        require(path.resolve(routePath))(app);
    });

    // Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
    app.use(function(err, req, res, next) {
        // If the error object doesn't exists
        if (!err) return next();

        // Log it
        console.error(err.stack);

        // Error page
        res.status(500).render('500', {
            error: err.stack
        });
    });

    // Assume 404 since no middleware responded
    app.use(function(req, res) {
        res.status(404).render('404', {
            url: req.originalUrl,
            error: 'Not Found'
        });
    });

    if (process.env.NODE_ENV === 'secure') {
        // Log SSL usage
        console.log('Securely using https protocol');

        // Load SSL key and certificate
        var privateKey = fs.readFileSync('./config/sslcerts/key.pem', 'utf8');
        var certificate = fs.readFileSync('./config/sslcerts/cert.pem', 'utf8');

        // Create HTTPS Server
        var httpsServer = https.createServer({
            key: privateKey,
            cert: certificate
        }, app);

        // Return HTTPS server instance
        return httpsServer;
    }

    // Return Express server instance
    return app;
};

Result of Console Log of Config Object

{ root: '/Users/yako/Developer/ronny/trunk/meanjs',
  app: { title: 'Dyad Medical - Development Environment' },
  sessionCookie: { maxAge: 86400000, httpOnly: true, secure: false },
  port: 3000,
  templateEngine: 'swig',
  sessionSecret: 'XXXXXXXXXX',
  sessionCollection: 'sessions',
  sessionKey: 'sessionId',
  assets: 
   { lib: { css: [Object], js: [Object] },
     css: [ 'public/modules/**/css/*.css' ],
     js: 
      [ 'public/config.js',
        'public/application.js',
        'public/modules/*/*.js',
        'public/modules/*/*[!tests]*/*.js' ],
     tests: 
      [ 'public/lib/angular-mocks/angular-mocks.js',
        'public/modules/*/tests/*.js' ] },
  db: 'mongodb://localhost/medical-dyad-dev',
  facebook: 
   { clientID: 'APP_ID',
     clientSecret: 'APP_SECRET',
     callbackURL: '/auth/facebook/callback' },
  twitter: 
   { clientID: 'CONSUMER_KEY',
     clientSecret: 'CONSUMER_SECRET',
     callbackURL: '/auth/twitter/callback' },
  google: 
   { clientID: 'APP_ID',
     clientSecret: 'APP_SECRET',
     callbackURL: '/auth/google/callback' },
  linkedin: 
   { clientID: 'APP_ID',
     clientSecret: 'APP_SECRET',
     callbackURL: '/auth/linkedin/callback' },
  github: 
   { clientID: 'APP_ID',
     clientSecret: 'APP_SECRET',
     callbackURL: '/auth/github/callback' },
  mailer: 
   { from: 'MAILER_FROM',
     options: { service: 'gmail', auth: [Object] } },
  seedDB: 
   { seed: false,
     options: { logResults: true, seedUser: [Object], seedAdmin: [Object] } },
  getGlobbedFiles: [Function],
  getJavaScriptAssets: [Function],
  getCSSAssets: [Function] }
like image 920
Daniel Kobe Avatar asked Apr 11 '16 09:04

Daniel Kobe


2 Answers

Your app has this setting for cookies:

    cookie: {
        maxAge: config.sessionCookie.maxAge,
        httpOnly: config.sessionCookie.httpOnly,
        secure: config.sessionCookie.secure && config.secure.ssl
    },

Since config.sessionCookie.secure is always false, then the line above will always resolve as false. Also, there is no setting anywhere for the property config.secure.ssl

If you are using HTTPS, then you should ensure that cookie.secure is set to true. One way to fix it is to get the SSL setting higher up in the code like this:

var secureConfig = (process.env.NODE_ENV === 'secure') ? true:false;

And then use that to set the cookie config line to the following:

    cookie: {
        maxAge: config.sessionCookie.maxAge,
        httpOnly: config.sessionCookie.httpOnly,
        secure: secureConfig
    },
like image 191
binarymax Avatar answered Nov 18 '22 08:11

binarymax


you need to save the user session data when user successfully login to Session Storage/Local Storage or global.window, so it become persistent even after the user refresh the page. From there you can always compare the client sessionId and mongoDb user sessionId using standard HTTP request.

like image 1
syarul Avatar answered Nov 18 '22 06:11

syarul