Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a new session after authentication with Passport

I've created a simple Authentication application with passport (see code below). Express through the session middleware, creates a session on every request where the requesting client does not have a session already. I'd like to assign sessions only after logging or create a new session after login.

This is because I will eventually be doing login over HTTPS and would like to prevent hackers from hijacking the sessions from users that have authenticated.

Here is my server code:

// Server.js configures the application and sets up the webserver

//importing our modules
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var MongoStore = require('connect-mongo')(express);

var configDB = require('./config/database.js');

//Configuration of Databse and App

mongoose.connect(configDB.url); //connect to our database

require('./config/passport')(passport); //pass passport for configuration

app.configure(function() {

    //set up our express application

    app.use(express.logger('dev')); //log every request to the console
    app.use(express.cookieParser()); //read cookies (needed for auth)
    app.use(express.bodyParser()); //get info from html forms
    app.set('view engine', 'ejs'); //set up ejs for templating

    //configuration for passport
    app.use(express.session({ secret: 'olhosvermdfgytuelhoseasenhaclassica',
         cookie: {
            maxAge: 120000 },
         store:
             new MongoStore({
                db: 'xYrotr4h',
                host: 'novus.modulusmongo.net',
                port: 27017,
                username: 'gdog',
                password: 'fakepassowrd123'
            })
         })); //session secret + expiration + store
    app.use(passport.initialize());
    app.use(passport.session()); //persistent login session
    app.use(flash()); //use connect-flash for flash messages stored in session

});

//Set up routes
require('./app/routes.js')(app, passport);

//launch
app.listen(port);
console.log("Server listening on port" + port);

In my new Passport local strategy I tried to use req.session.regenerate() or req.session.reload() when the user was successfully validated against the database but that caused the server to crash.

Here is how I define my strategy:

//Passport.js sets up our local strategies

//imports

var LocalStrategy = require('passport-local').Strategy;
var User = require('../app/models/user');

//export this as a module since we give it to passport

module.exports = function(passport) {
    //Set up the session for persistent login

    passport.serializeUser(function(user, done) {
        done(null, user.id);
    });

    //used to serialize the user
    passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });

    //setting up local sign up

    passport.use('local-signup', new LocalStrategy({
            //by default, the local strategy uses usernames and password, we will override with email
            usernameField: 'email',
            passwordField: 'password',
            passReqToCallback: true
        },
        function(req, email, password, done) {
            console.log("Callback ran!");
            //asynchronous
            //User.findOne wont fire unless data is sent back
            process.nextTick(function() {
                console.log("I did run!");
                //find user whose email is the same as form email
                // we are checking to see if the user trying to sign up already exists
                User.findOne({ 'local.email': email }, function(err, user) {
                    //if there any errors, return the errors
                    if (err) {
                        return done(err);
                    }
                    //check to see if there any users already with that email
                    if (user) {
                        return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
                    } else {
                        console.log('New user will be added to the DB!');
                        //if there is no user with that e-mail, create the user
                        var newUser = new User();

                        //we set the user's local credentials
                        newUser.local.email = email;
                        newUser.local.password = newUser.generateHash(password);

                        //save the user in the store
                        newUser.save(function(err) {
                            if (err) {
                                throw err;
                            }
                            return done(null, newUser);
                        });
                    }
                });
            });
        }));

        // =========================================================================
    // LOCAL LOGIN =============================================================
    // =========================================================================
    // we are using named strategies since we have one for login and one for signup
    // by default, if there was no name, it would just be called 'local'

    passport.use('local-login', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true // allows us to pass back the entire request to the callback
    },
    function(req, email, password, done) { // callback with email and password from our form

        // find a user whose email is the same as the forms email
        // we are checking to see if the user trying to login already exists
        User.findOne({ 'local.email' :  email }, function(err, user) {
            // if there are any errors, return the error before anything else
            if (err)
                return done(err);

            // if no user is found, return the message
            if (!user)
                return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash

            // if the user is found but the password is wrong
            if (!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            // removing the req.session.regenerate fixes any crashing
            req.session.regenerate(function(err, done, user){
                    return done(null, user);
                 });

        });

    }));

};
like image 842
Georges Krinker Avatar asked Mar 05 '14 21:03

Georges Krinker


1 Answers

After digging into passports and express-sessions library, I've figured it out!

var session = function (req, res) {
    var temp = req.session.passport; // {user: 1}
    req.session.regenerate(function(err){
        //req.session.passport is now undefined
        req.session.passport = temp;
        req.session.save(function(err){
            res.send(200);
        });
    });
};

app.post('/login', passport.authenticate('local'), session);

Basically I let passport do its authentication first, where it attaches an object onto req.session.passport. Passport uses this object to find out the mapping from session -> userId on further requests. When you regenerate the session, the req.session.passport object gets lost. Thus, you have to make sure to transfer it over to the newly generated session, and save it.

like image 134
Steven Yang Avatar answered Sep 21 '22 18:09

Steven Yang