Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript User Authentication With Express and Mongo Improving the App with Custom Middleware Using MongoDB as a Session Store

Mike Lundahl
Mike Lundahl
13,510 Points

Using MongoDB as a Session Store

Thanks for a great course so far...

I'm a bit stuck.. everything worked fine following all the steps up until I got to this stage. I'm getting the error message of:

"TypeError: Cannot set property 'userId' of undefined"

I've been trying to backtrack and look for a solution for a couple of days but just can't figure it out. Any advice for what it could be or where I should look?

Also as a side note. I also had the message of:

"WARNING: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead"

that I solved by adding "mongoose.Promise = global.Promise;" right before the mongo connection.

Don't know when this course was recorder but might it be that features used in the videos are outdated? (having similar issues with the react basics course, but that's another discussion)

Thanks!

Seth Kroger
Seth Kroger
56,413 Points

Perhaps if you shared the relevant code (the file(s) you edited between working and not working) we could take a look? There could be an issue you're missing.

Mike Lundahl
Mike Lundahl
13,510 Points

Hi Seth,

Here's how the code looks like currently... I don't have the code from when it was working, however it worked fine storing the session to memory and not mongoDB.

app.js

var babel = require("babel-core");
var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var app = express();

//mongo connection
var dbConn = require('./routes/database');
var db = dbConn.db;

//Parse incoming requests
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false}));



//file loads
// fs.readdirSync(__dirname + '/models/').forEach(function(filename) {
//    if (~filename.indexOf('.js')) require(__dirname + '/models/' + filename)
// });

//Include models
var User = require('./models/user')

//Include routes
var routes = require('./routes/index');
app.use('/', routes);

//serve static files from /static
app.use(express.static(__dirname + '/static'));

// use session for tracking logins
app.use(session({
    secret: 'storm is awesome',
    resave: true,
    saveUninitialized: false,
    store: new MongoStore({
        mongooseConnection: db
    })
}));


app.get('/', function(req, res) {
    res.sendFile(__dirname + '/statics/html/register.html');
});

var server = app.listen(8080, function () {
        console.log('server is running at http://localhost:' + server.address().port);
});

Database connection

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var MongoClient = require('mongodb').MongoClient;
var mongoose = require('mongoose');

var db;
var error;
var waiting = [];

mongoose.Promise = global.Promise;

//mongo connection
mongoose.connect("mongodb://localhost:27017/storm", function(err, database) {
    error = err;
    db = database;

    waiting.forEach(function(callback) {
    callback(err, database);
  });


     if(!err) {
    console.log("We are connected to stormdb");
  }
});

var db = mongoose.connection;

module.exports.db = db;

The middleware

// Mongo Session middleware

function loggedOut(req, res, next) {
    if(req.session && req.session.userId) {
        return res.redirect('/profile');
    }
    return next();
}

function loginRequired(req, res, next) {
    if(req.session && req.session.userId) {
        var err = new Error("You must be logged in to view this page.");
        err.status = 403;
        return next(err);
    }
}
module.exports.loggedOut = loggedOut;
module.exports.loginRequired = loginRequired;

The model, user.js

// JavaScript Document

var mongoose = require('mongoose');
var bcryptjs = require('bcryptjs');
var UserSchema = new mongoose.Schema({
      userName: {
    type: String,
    unique: true,
    required: true,
    trim: true
    },
     email: {
    type: String,
    unique: true,
    required: true,
    trim: true
    },
     firstName: {
    type: String,
    unique: true,
    required: true,
    trim: true
    },
     lastName: {
    type: String,
    unique: true,
    required: true,
    trim: true
    },
     password: {
    type: String,
    unique: true,
    required: true
    }

});

// authenticate input against database documents
UserSchema.statics.authenticate = function (email, password, callback) {
    User.findOne({ email: email })
    .exec(function (error, user) {
        if (error) {
            return callback(error);
        } else if ( !user ) {
            var err = new Error('User not found.');
            err.status = 401;
            return callback(err);
        }
        bcryptjs.compare(password, user.password, function (error, result) {
                if (result === true) {
                    return callback(null, user);
                } else {
                    return callback();
                }
            })
    });

};
//hash password before saving to database
UserSchema.pre('save', function (next) {
   var user = this; 
    bcryptjs.hash(user.password, 10, function (err, hash) {
        if (err) {
            return next(err);
        }
        user.password = hash;
        next();
    })

});

//User model

var User = mongoose.model('User', UserSchema);
module.exports = User;

Routes

// JavaScript Document

var express = require('express');
var router = express.Router();
var User = require('../models/user');
var path = require('path');
var mid = require('../middleware')



// GET / login
router.get('/login', mid.loggedOut, function(req, res, next) {
    return res.sendFile(path.join(__dirname, '../statics' ,'/html', 'login.html'));

});

// POST / login
router.post('/login', function(req, res, next) {
    if (req.body.email && req.body.password ) {
        User.authenticate(req.body.email, req.body.password, function (error, user) {
            if (error || !user) {
                var err = new Error ('Wrong email or password!');
                err.status = 401;
                return next(err);
            } else {
                req.session.userId = user._id;
                return res.redirect('/profile');
                //return res.send('logged in!');
            }

        });

    } else {

        var err = new Error('Email and password are required!')
        err.status = 401;
        return next(err);        
    }

});

// GET / logout
router.get('/logout', function (req, res, next) {
    if (req.session) {
        //delete the session
        req.session.destroy(function(err) {
            if(err) {
                return next(err);
            } else {
                return res.redirect('/');
            }
        })
    }
})

// GET / profile
router.get('/profile', mid.loginRequired, function(req, res, next) {
    User.findById(req.session.userId)
    .exec(function (error, user) {
        if (error) {
            return next (error);
        } else {
            return res.sendFile(path.join(__dirname, '../statics', '/html', 'profile.html'));
        }

    })
 });

And here's the package.js

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "./index.html",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack-dev-server --progess --inline --hot"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.0",
    "body-parser": "^1.15.2",
    "connect-mongo": "^1.3.2",
    "express": "^4.14.0",
    "express-session": "^1.14.2",
    "mongodb": "^2.2.15",
    "mongoose": "^4.7.3",
    "react": "^15.4.1",
    "react-dom": "^15.4.1"
  },
  "devDependencies": {
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "react-hot-loader": "^1.3.1",
    "webpack": "^1.14.0",
    "webpack-dev-server": "^1.16.2",
    "webpack-node-externals": "^1.5.4"
  }
}

1 Answer

Seth Kroger
Seth Kroger
56,413 Points

I think the issue is that you put the app.use() for the session middleware after your router instead of before it. Since the router will be executed first, the session data won't be available to it.

Mike Lundahl
Mike Lundahl
13,510 Points

Huge thanks Seth! That worked!