Node Tutorial Part 9

2011-01-10 00:00:00 +0000 by Alex R. Young

Welcome to part 9 of Let's Make a Web App, a tutorial series about
building a web app with Node. This series will
walk you through the major areas you'll need to face when building your
own applications. These tutorials are tagged with

Previous tutorials:

Updating connect-mongodb

If you remember back to the start of this series, I had to write a hack
to map a mongo connection string to the format
connect-mongodb expected. I contacted the author about this through GitHub, and he promptly
updated the library to work with connection strings. That means we can
scrap mongoStoreConnectionArgs.

Install the version of the package that I'm using:

npm install connect-mongodb@0.1.1

Now update

// This is near the top of the file in the var declaration
mongoStore = require('connect-mongodb@0.1.1')

// The mongoStoreConnectionArgs function can be removed

// In the app configure block, setting up connect.mongodb looks like this
app.use(express.session({ store: mongoStore(app.set('db-uri')) }));

Remember Me Functionality

Making logins persist in web apps involves some server-side work. It
usually works like this:

  1. When people log in, an extra "remember me" cookie is created
  2. The cookie contains the username and two random numbers (a series
    token and a random token)
  3. These values are also stored in the database
  4. When someone visits the site who isn't logged in, if the cookie is
    present it's checked against the database. The token is updated and sent back to the user
  5. If the username matches but the tokens do not, the user is sent a
    warning and all sessions are removed
  6. Else the cookie is ignored

This scheme is designed to protect against cookie theft, and is
described by Barry Jaspan in Improved Persistent Login Cookie Best

Building Remember Me

In the models.js file I've added a LoginToken model:

mongoose.model('LoginToken', {
  properties: ['email', 'series', 'token'],

  indexes: [

  methods: {
    randomToken: function() {
      return Math.round((new Date().valueOf() * Math.random())) + '';

    save: function() {
      // Automatically create the tokens
      this.token = this.randomToken();
      this.series = this.randomToken();

  getters: {
    id: function() {
      return this._id.toHexString();

exports.LoginToken = function(db) {
  return db.model('LoginToken');

// Load from app.js like this:
// app.LoginToken = LoginToken = require('./models.js').LoginToken(db);

This is basic Mongoose stuff. It will automatically create the tokens
when the model is saved.


Now let's add a simple bit of Jade to views/sessions/new.jade:

  label(for='remember_me') Remember me:
  input#remember_me(type='checkbox', name='remember_me')


The session POST method should be updated to create a
LoginToken if required:

app.post('/sessions', function(req, res) {
  User.find({ email: req.body.user.email }).first(function(user) {
    if (user && user.authenticate(req.body.user.password)) {
      req.session.user_id = user.id;

      // Remember me
      if (req.body.remember_me) {
        var loginToken = new LoginToken({ email: user.email });
        loginToken.save(function() {
          res.cookie('logintoken', loginToken.cookieValue, { expires: new Date(Date.now() + 2 * 604800000), path: '/' });

    } else {
      req.flash('error', 'Incorrect credentials');

And the tokens should be removed when logging out:

app.del('/sessions', loadUser, function(req, res) {
  if (req.session) {
    LoginToken.remove({ email: req.currentUser.email }, function() {});
    req.session.destroy(function() {});

Express Cookie Tips

The basic Express cookie API works like this:

// Create a cookie:
res.cookie('key', 'value');

// Read a cookie:

// Delete a cookie:

The cookie names will always be lowercase. Notice that any write
operations are performed on the result being sent back to the browser
(res), and read operations are through an object on the request, req

Updating the loadUser Middleware

Now we need to make loadUser check if a
LoginToken is present:

function authenticateFromLoginToken(req, res, next) {
  var cookie = JSON.parse(req.cookies.logintoken);

  LoginToken.find({ email: cookie.email,
                    series: cookie.series,
                    token: cookie.token })
            .first(function(token) {
    if (!token) {

    User.find({ email: token.email }).first(function(user) {
      if (user) {
        req.session.user_id = user.id;
        req.currentUser = user;

        token.token = token.randomToken();
        token.save(function() {
          res.cookie('logintoken', token.cookieValue, { expires: new Date(Date.now() + 2 * 604800000), path: '/' });
      } else {

function loadUser(req, res, next) {
  if (req.session.user_id) {
    User.findById(req.session.user_id, function(user) {
      if (user) {
        req.currentUser = user;
      } else {
  } else if (req.cookies.logintoken) {
    authenticateFromLoginToken(req, res, next);
  } else {

Notice that I've put the LoginToken code in its own
function. That helps keep loadUser readable.


This is a slightly simplified version of the method suggested by Barry
Jaspan, but it's fairly easy to follow and demonstrates fairly advanced
Express cookie handling.

The version of the code I've checked in for part 9 is commit