0

I'm making a SPA with login functionality. I enacted session handling with 'express-session' originally so that the user's mongoDB _id would be stored, and they could hit 'refresh' without being logged out, which worked perfectly well.

From here, I want to allow the use to check a "remember me" box that will control long-term site (re)visits. So, if 'remember me' is not checked, during a given visit, the session handling works the same ('refresh button' maintains login), but session data is lost once they close the browser. Conversely, checking 'remember me' stores their login data in my database (using 'connect-mongo' for this), maintaining login between full server visits (days, weeks, etc.).

The problem seems to be that the browser's session cookie and mongodb session entry are intrinsically tied together, so I can't alter/eliminate one without affecting the other. If that's even the right approach?

So here I get the everything I need:

var express = require('express');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);

var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server);

var fs = require('fs');
var bodyParser = require('body-parser');

var dbHdlr = require('./Server/dbHdlr.js')();

... set up session handling with storage...

var mongoStore = new MongoStore({ 
    url: 'mongodb://' + dbHdlr.GetConnectionURL(),
    ttl: 7 * 24 * 60 * 60, // Session saves for 7 days
});
var SessionFunc = session({
    secret: 'SomeKindOfSessionSecretIReallyDontKnowMuchAbout',
    cookie: {
        maxAge: 7 * 24 * 60 * 60 * 1000 // Cookie expires in 7 days
    },
    saveUninitialized: false,
    resave: false,
    store: mongoStore
});
app.use('/' , SessionFunc);

... Apply login functionality with 'remember me' checkbox (using "persist" property in this context to mean long-term persistence) ...

app.post('/LoginData', function(req, res){
    dbHdlr.GetAccountID({ email: req.body.email, password: req.body.password }, function(resObj) {
        if(resObj.success) {    
            req.session.accountID = resObj.accountID;
            if(req.body.remember)
                req.session.persist = true;
            else
                req.session.persist = false;        
        }
        res.json(resObj);
    });
});

... a single point of entry where i create a page initially based solely on login status. And this is where I figured that if they choose not to have long-term persistence, I would simply remove the entry from the store? Or reset the ttl to one second maybe? I don't know how to make this work. Removing the store entry kills the whole thing so that I can't even hit 'refresh' without being logged out, even though that wasn't a problem before the store was ever even implemented ...

app.get('/', function (req, res) {

    if(req.session.accountID) {
        res.render('index.ejs', { loggedIn: true });
        if(!req.session.persist)
            mongoStore.destroy(req.sessionID, function(error) {});
    }
    else
        res.render('index.ejs', { loggedIn: false });
});

Thoughts? Is this possible to achieve without explicitly programatically knowing when the user hits 'refresh' versus changing sites/closing the browser?

Sleipnir
  • 1
  • 1

2 Answers2

0

What you can do is set the cookie expiration time explicitly based on if the remember box is checked or not. If the box is not checked make it a session cookie if it is then do not do anything since the default cookie expiration time has been set to 7 days by you.

You can do it like this I suppose :

app.post('/LoginData', function(req, res){
    dbHdlr.GetAccountID({ email: req.body.email, password: req.body.password }, function(resObj) {
        if(resObj.success) {    
            req.session.accountID = resObj.accountID;

            if(!req.body.remember)//note the ! here which makes condtion a negation
            {
                //if box is not checked that means now the cookie should be a non persistent, i.e., session cookie
                req.session.cookie.expires = null;
                req.session.cookie.maxAge = null;
                //ps i did not test if this works or not but theoratically based on documentation it should
                // if it doesnt try setting it to 0 instead of null
            }                 
        }
        res.json(resObj);
    });
});

And now for rendering a SPA based on whether the user is logged in or not you just need to do this :

app.get('/', function (req, res) {

    if(req.session.accountID)
        res.render('index.ejs', { loggedIn: true });   
    else
        res.render('index.ejs', { loggedIn: false });
});

Now why we did this is because the cookie will expire automatically when browser is closed so user is logged out when browser is closed since that session cookie is not stored anymore in his/her browser.

Zeus
  • 1,235
  • 12
  • 20
  • I tried it, no dice. I probably should have mentioned that this was one of the first things I tried. Either the entire session persists, short & long term, or it doesn't at all. – Sleipnir Jun 23 '17 at 17:55
  • @Sleipnir try setting the rolling option to true in the same part where you set saveUninitialized to false. Like `...rolling: true, saveUninitalized:false....`. Then try this solution again. If it doesn't work then I would recommend to change your session handling a bit and write your own session handling function instead of using express-sessions. I wrote one myself a long time ago, if this doesn't work hit me up i will upload that one(oh and that one is in MySQL not mongo sadly) – Zeus Jun 23 '17 at 20:06
  • It more-or-less does work now, but with 1 major problem: every login without selecting "remember me" results in a new store entry, all of which have the same 2 week expiration date, which I cannot seem to change. I'm pulling my hair out trying to sift through every option available to dynamically change the store ttl of a given entry, and nothing works. – Sleipnir Jun 23 '17 at 21:54
  • @Sleipnir Well thats why i left using express-sessions and now currently though I have not modified my projects to use this I plan on storing non persistent data in REDIS ( its a lightning fast in-memory storage ) with expiration time of 5 mins and persistent one in mongo/mysql with a long (in months) expiration time. – Zeus Jun 24 '17 at 15:27
  • Ultimately I decided to scrap the "remember me" button and just persist session until they actively logout. If I can't get a specific call to the server indicating when someone enters/exits my domain (separate of hitting refresh), than there's really no clean way to manage this. – Sleipnir Jun 25 '17 at 00:08
0

maybe you want to use the localstorage, and when you reload the navigator (or re-open) execute a function that send a (maybe) token with the user id, and provide access for you main menu/home

Edgar Olivar
  • 1,348
  • 13
  • 13