Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Express-session not persistent after redirect

Yesterday, I tried to make a website, using Node.js and the framework Express.js. On this website, the user needs to log, with credentials, who are checked in database. If the credentials are correct, I want to save the user's informations in a session.

In this aim, I use the middleware Express-Session, but I have a problem. When the user types good credentials, its informations are correctly stored in the session. However, after redirecting the user to the homepage, the session is cleared, so the variable who stores the user's informations is now undefined.

I tried many solutions, and I searched a lot, but I didn't reached to fix this problem...

There is my code : app.js :

const createError = require('http-errors');
const express = require('express');
const session = require('express-session');
const path = require('path');
const helmet = require('helmet');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const bodyParser = require('body-parser');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const admRouter = require('./routes/adm');

const urlencodedParser = bodyParser.urlencoded({extended : false});

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(urlencodedParser);
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({secret: 'secret', resave: false, saveUninitialized: false, cookie: { maxAge : 60000, secure: false }}));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/adm', admRouter);

// On utilise helmet pour sécuriser l'application.
app.use(helmet());

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render('error');
});

module.exports = app;

app.listen(80);

And index.js :

const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const verif = require('../functions/verif');
const password = require('node-php-password');

const dbServer = require('../database');
// const credentials = require('../functions/dbCredentials');

// const dbServer = mysql.createConnection(credentials);

const urlencodedParser = bodyParser.urlencoded({extended : false});

/* GET home page. */
router.get('/', function(req, res, next) {
    console.log(JSON.stringify(req.session.user));
    res.render('index', {verif, req });
});

router.post('/login', urlencodedParser, (req, res, next) => {
    if(req.body.username !== undefined && req.body.password !== undefined) {
        if(req.body.username !== null && req.body.password !== null) {
            dbServer.query('SELECT * FROM users WHERE username = ?', [req.body.username], function(error, result, fields) {
                if (error) throw error;
                // if(password.verify(req.body.password, result))
                console.log("resultat : " + JSON.stringify(result));
                if(result.length > 0) {
                    const utilisateur = result[0]; // On stocke la ligne concernant l'utilisateur dans une constante locale.
                    console.log("L'utilisateur existe.");
                    // On teste le résultat obtenu, pour savoir si son mot de passe est correct.
                    if(password.verify(req.body.password, utilisateur.password)) {
                        console.log("Mot de passe correct.");
                        req.session.user = utilisateur;
                        console.log(req.session.user);
                    } else {
                        // TODO : Session, pour afficher l'erreur.
                        console.log("Mot de passe incorrect.");
                    }
                }
                else {
                    console.log("L'utilisateur n'existe pas.")
                    // TODO : Session, pour afficher l'erreur.
                }
            });
        }
    }
    res.redirect('/');
});

module.exports = router;

With this code, when the user logs in, the :

console.log(req.session.user);

displays correct informations, but the line :

console.log(JSON.stringify(req.session.user));

for the route '/' displays "undefined".

So, I'm a bit lost in this situations... Do you have ideas to fix this problem ? Thanks by advance =)

like image 563
Yan Buatois Avatar asked Mar 25 '18 12:03

Yan Buatois


3 Answers

The problem is, you are redirecting them way too early. DB queries take much longer than most code ran in a server, which is why callbacks and promises are used. I would move the res.redirect('/') right at the end of your database query. Right after the last else statement. This should fix it as it allows everything to run before a redirection is made.

like image 89
Cruiz Kusman Avatar answered Oct 19 '22 16:10

Cruiz Kusman


You're using the implicit in memory store for express-session. They recommend that this should not be used in production, however my experience with it is that it is compeltely unstable even in simple dev environments. I tried redirecting in a timeout setTimeout(() => res.redirect('/'), 1000) and statistically it failed in less the amount of cases then if I had called it directly, though this is far form ideal.

In my current case I ended up implementing my own dumb memory store just to move things forward and will later switch to a database backend one.

LATER EDIT: I did some extensive testing and it seems that in my case there might be some racing conditions between setting the session cookie and redirecting, I think sometimes the browser redirects before it gets to set the sid thing. I managed to find this out by logging the active sesions at each step during my actions, and it seems new sessions are being created on redirects for no apparent reason.

like image 39
Vee6 Avatar answered Oct 19 '22 14:10

Vee6


I have encountered this problem and solved it through a hint given by the express-session readme.

If you will look at the https://github.com/expressjs/session#cookiesecure section of readme.md then you will notice this statement:

Please note that secure: true is a recommended option. However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies. If secure is set, and you access your site over HTTP, the cookie will not be set.

Thus, I attempted the following:

const sess = {
    secret: 'My app',
    store: MongoStore.create({ 
        mongoUrl: 'mongodb://localhost:27017/myBaseApp',
        crypto: {
            secret: 'My database'
        }
    }),
    resave: true,
    saveUninitialized: false,
    cookie: { 
        secure: false,            //setting this false for http connections
        maxAge: 3600000,
        expires: new Date(Date.now() + 3600000) 
    }
}

And the redirect started working fine.

like image 2
user116034 Avatar answered Oct 19 '22 14:10

user116034