Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Express cookieSession and Mongoose: how can I make request.session.user be a Mongoose model?

When a new user account is created, I create newUser, a Mongoose model instance that looks like:

_events: Object
errors: undefined
isNew: false
save: function () {
arguments: null
caller: null
_doc: Object
  name: 'Joe Smith'
...

The actual data for the object is in the _doc property, though getters and setters exist so you can run:

user.name = 'Jane Doe' 

and that will work fine. I run:

request.session.user = newUser;

To save the user to the session. So far, so good.

However, in subsequent requests, request.session.user seems to be only the stuff in _doc. Eg:

name: 'Joe Smith'

Which is nice, but that means I can't run eg, request.session.user.save() to save changes.

I could simply make some middleware to look up the user associated with the data. But I'd like to know more about what Express and Mongoose are doing here.

How can I make request.session.user be a Mongoose model?

Update: my current middleware hack:

// Use as a middleware on routes which need users
// TODO: only needed due to request.session.user being saved weirdly
var rehydrateUser = function(request, response, next) {
  if ( request.session.user ) {
    var hydrated = request.session.user.save
    if ( ! hydrated ) {
      console.log('Rehydrating user...');
      models.User.findOne({ somefield: request.session.user.somefield }, function (err, user) {
        if ( err ) {
          request.session.error = 'User not found in DB!';
          request.redirect('/');
        } else {
          request.session.user = user;          
          console.log('Rehydrated user! All repos already being monitored.');
          next();
        }
      })
    } else {
      next();
    }
  } else {
    console.log('No user in session')
    next();
  }
}
like image 825
mikemaccana Avatar asked Aug 29 '13 13:08

mikemaccana


People also ask

What is the difference between a Mongoose schema and model?

A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.

What is the difference between Express session and cookie session?

Cookie session is basically used for lightweight session applications where the session data is stored in a cookie but within the client [browser], whereas, Express Session stores just a mere session identifier within a cookie in the client end, whilst storing the session data entirely on the server.

Can I use $in in Mongoose?

For example, if we want every document where the value of the name field is more than one value, then what? For such cases, mongoose provides the $in operator. In this article, we will discuss how to use $in operator. We will use the $in method on the kennel collection.

Which dependency is required in the Express app to use Mongoose?

Mongoose requires a connection to a MongoDB database. You can require() and connect to a locally hosted database with mongoose. connect() as shown below (for the tutorial we'll instead connect to an internet-hosted database). You can get the default Connection object with mongoose.


1 Answers

Express session is stored as separate storage and has limitations to what can be stored there. It cannot store prototype data (methods). Look at it more like key<>value storage.

Reasons for this is that session can be stored anywhere (pretty much), such as: process memory; databases (mongodb); redis; etc.

Because of this, once you put anything in session object and request is finished, express will take session data, parse it and stick into session store just as pure object. By default it is process memory, but as mentioned before - can be any other storages.

So when next http request happens, session middleware will try to restore session using cookie (if it is used) data in request headers, and will ask session storage to provide data back. As it is already formatted - it will not have .prototype stuff but just data (object, array, string, number, and other simple stuff).

So you need to create Model out of that data again.
You can do by middleware in express. So in app.configure straight after defining session you can do this:

app.use(function(req, res, next) {
  if (req.session && req.session.user) {
    req.session.user = new UserModel(req.session.user);
  }
  return next();
});

This will check if session is defined as well as user. And then will recreate mongoose model out of that data.

As mentioned here in comments - you should not store user data in session as it will lead to inconsistencies due to split of responsibilities over ownership of data. So store just ID of user, and then using middleware load it from Database.

app.use(function(req, res, next) {
  if (req.session && req.session.userID) {
    UserModel.findById(req.session.userID, function(err, user) {
      if (!err && user) {
        req.user = user;
        next();
      } else {
        next(new Error('Could not restore User from Session.'));
      }
    });
  } else {
    next();
  }
});

After you can create middleware to check if user is logged in:

function userRequired(req, res, next) {
  if (req.user) {
    next();
  } else {
    next(new Error('Not Logged In');
  }
}

And use it like that:

app.get('/items', userRequired, function(req, res, next) {
  // will only come here if user is logged in (req.user !== undefined)
});
like image 93
moka Avatar answered Oct 03 '22 14:10

moka