Question

I'm building a simple site with expressjs and passportjs and I'm running into the problem that I can't access session variables in my routes.

There are a lot of threads with this topic but the solutions don't work for me. It looks like my error is somewhere else.

My app is configured like this:

app.configure(function() {
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.logger());
    app.use(express.cookieParser());
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.session({
        path: '/',
        secret: 'very secret'
    }));

    app.use(passport.initialize());
    app.use(passport.session());
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

Once passport verified the twitter account it's redirected to this site:

app.get('/auth/twitter/callback',
    passport.authenticate('twitter', {
        failureRedirect: '/login'
    }),
    function(req, res) {
        res.redirect('/');
        console.log("populate session")
        populateSession(req, res);
    });

This works, as I'm seeing the "populate session" output in the console if I'm logged in.

The populateSession() looks like this:

function populateSession(req, res) {
    User.findOne({
        twitterID: req.user.id
    }, function(err, result) {
        if (result) {
            // Store data to session
            req.session.twitterAccessToken = result.twitterAccessToken;
            req.session.twitterAccessTokenSecret = result.twitterAccessTokenSecret;
            req.session.lastfmAccountName = result.lastfmAccountName;

            console.log("in session: " + req.session.lastfmAccountName)
        }
    })
}

"in session" is printed the right way. So the session itself works. Now my problem is that I want to have access to my session variables in routes because I want to pass them to my view templates like this:

app.get('/account', ensureAuthenticated, function(req, res) {
    console.log(req.session)
    console.log("lastfm nick " + req.session.lastfmAccountName)
    res.render('account', {
        user: req.user,
        lastfmnick: req.session.lastfmAccountName
    });

That's where I'm running into the problems. req.session contains all the twitter fields passport is populating it with but req.session.lastfmAccountName is undefined.

Any idea what's wrong there or is there a better way to pass variables to the view? I feel like it's not a good idea to have DB queries for the fields in all my routes if it could just be stored in the session.

Thanks!

Was it helpful?

Solution

The session will automatically be saved when the response ends, in this case by res.redirect(), which is done before the modifications are made to the session.

function(req, res) {
    res.redirect('/');         // ends the response, saving the session

    populateSession(req, res); // modifies the session without saving
});

Since .findOne() is asynchronous, if you revise populateSession() to take and call a callback when the find completes, you can control the order so the session is modified first:

function populateSession(req, res, next) {
    User.findOne({
        twitterID: req.user.id
    }, function(err, result) {
        if (result) {
            // ...
        }

        if (next) next(err);
    })
}
app.get('/auth/twitter/callback',
    /* ... */,
    function(req, res) {
        populateSession(req, res, function () {
            res.redirect('/');
        });
    });

It also allows you to use populateSession as middleware:

app.get('/auth/twitter/callback',
    /* ... */,
    populateSession,
    function(req, res) {
        res.redirect('/');
    });

OTHER TIPS

 app.use(lib.express.cookieParser(lib.config.cookieSecret));
 app.use(lib.express.session({
secret: 'very secret'
 })
 }));

This two line should be consecutive and in that order.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top