Node.js — Getting oAuth Up and Running Using Express.js, Railway.js and Mongoose

Stamped: 10 Oct 2011 | Show comments

Let's get started

If you're interested in how to incorporate this without using railwayjs, see this other article instead.

This is assuming you already have a working railwayjs project working. If not:

sudo npm install railway -g
rw i blog && cd blog
npm install -l
rw g crud post title content
# make sure mongodb is running
rw s 3000
open http://localhost:3000/posts

If you need to install mongodb, it's simple. In Ubuntu, you can just apt-get install mongodb, or in osx, brew install mongodb;. If you need to install homebrew for OSX, /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)".

Setting up the environment

First things first, change your package.json to include mongoose-auth.

{ "name": "railwayjs project"
, "version": "0.0.1"
, "engines": ["node >= 0.4.0"]
, "main": "server.js"
, "dependencies":
  { "jade-ext":  ">= 0"
  , "jade":      ">= 0"
  , "express":          ">= 2.2.2"
  , "connect":          ">= 1.4.2"
  , "railway":          ">= 0.1.6"
  , "yaml":             ">= 0.1.2"
  , "coffee-script":    ">= 1.1.1"
  , "mime":             ">= 1.2.2"
  , "qs":               ">= 0.1.0"
  , "mongoose":         ">= 1.3.6"
  , "mongodb":          ">= 0.9.4-5"
  , "connect-mongodb":  ">= 0.3.0"
  , "mongoose-auth":    ">= 0.0.2"
  }
, "scripts":
  { "test": "nodeunit test/*/*"
  }
}

Next, we're going to change our config/environment.js to look like the following:

var express = require('express');
var mongooseAuth = require('mongoose-auth');

require('./mongoose_oauth');

app.configure(function() {
  var cwd;
  cwd = process.cwd();
  app.set('views', cwd + '/app/views');
  app.set('view engine', 'jade');
  app.use(express.static(cwd + '/public', {
    maxAge: 86400000
  }));
  app.use(express.bodyParser());
  app.use(express.cookieParser());
  app.use(express.session({
    secret: 'secret'
  }));
  // MAKE SURE THIS IS COMMENTED OUT, otherwise it will produce errors that are mostly nonsensical
  //app.use(app.router)
  app.use(express.methodOverride());
  app.use(mongooseAuth.middleware());
});
mongooseAuth.helpExpress(app);

Notice the use of the require statement, require('./mongoose_oauth');, this files contents are here and look like:

var mongoose = require('mongoose')
  , Schema = mongoose.Schema;

var conf = require('./oauth_providers');

var UserSchema = new Schema({})
  , User;

mongooseAuth = require('mongoose-auth');

UserSchema.plugin(mongooseAuth, {
  everymodule: {
    everyauth: {
      User: function() {
        return User;
      }
    }
  },
  facebook: {
    everyauth: {
      myHostname: 'http://local.host:3001',
      appId: conf.fb.appId,
      appSecret: conf.fb.appSecret,
      redirectPath: '/'
    }
  },
  twitter: {
    everyauth: {
      myHostname: 'http://local.host:3001',
      consumerKey: conf.twit.consumerKey,
      consumerSecret: conf.twit.consumerSecret,
      redirectPath: '/'
    }
  },
  github: {
    everyauth: {
      myHostname: 'http://local.host:3001',
      appId: conf.github.appId,
      appSecret: conf.github.appSecret,
      redirectPath: '/'
    }
  }
});
User = mongoose.model('User', UserSchema);
module.exports["User"] = mongoose.model("User");
module.exports["User"].modelName = "User";

Also notice, the configuration file, config/oauth_providers.js, this looks like:

module.exports = {
  fb: {
    appId: '111565172259433',
    appSecret: '85f7e0a0cc804886180b887c1f04a3c1'
  },
  twit: {
    consumerKey: 'JLCGyLzuOK1BjnKPKGyQ',
    consumerSecret: 'GNqKfPqtzOcsCtFbGTMqinoATHvBcy1nzCTimeA9M0'
  },
  github: {
    appId: '11932f2b6d05d2a5fa18',
    appSecret: '2603d1bc663b74d6732500c1e9ad05b0f4013593'
  },
  instagram: {
    clientId: 'be147b077ddf49368d6fb5cf3112b9e0',
    clientSecret: 'b65ad83daed242c0aa059ffae42feddd'
  },
};

NOTE: these keypairs really shouldn't be made public, but they exist in the mongoose-auth repo, so you can test using them. This also assumes you have something setup in /etc/hosts to map local.host to 127.0.0.1, example: 127.0.0.1 local.host

The views

Now, if you were to run your app, rw s 3000, and go to http://local.host:3000/auth/twitter, you would find, after logging in to twitter, it redirects you back to the homepage, as per the mongoose_oauth.js config. But how do you know if you're actually logged in, and the user is being stored in the session?

Well, we have to update a view to contain the following information:

- var items = ["facebook", "github", "twitter", "instagram"]
- if (!everyauth.loggedIn)
  h2 Not Authenticated
  each item in items
   a(href='/auth/' + item)
     span Connect with <span style="text-transform: capitalize">!{item}</span><br />

- else
  h2 Authenticated
  #user-id Logged in with `user.id` #{user.id} - aka `everyauth.user.id` #{everyauth.user.id}
  - if (everyauth.facebook)
    h3 Facebook User Data
    p= JSON.stringify(everyauth.facebook.user)
  - if (everyauth.twitter)
    h3 Twitter User Data
    p= JSON.stringify(everyauth.twitter.user)
  - if (everyauth.github)
    h3 GitHub User Data
    p= JSON.stringify(everyauth.github.user)
  - if (everyauth.instagram)
    h3 Instagram User Data
    p= JSON.stringify(everyauth.instagram.user)
  h3
    a(href='/logout') Logout

And Done!

Now you should see various metadata depending on how you're logged in. All the files above can be found in this gist.

tags: nodejs, oauth, railwayjs, expressjs
recent entries
Rails — A faster way for next and previous links on a post, article, or any model
The awkward things Siri says
Node.js — Getting oAuth Up and Running Using Express.js and Mongoose
Node.js — Getting oAuth Up and Running Using Express.js, Railway.js and Mongoose
Migrating from Rails 3.1 RC4 to RC5 using Heroku's Cedar Stack (also compass, unicorn, and sendgrid)
Random Freeze Fix for GTX 460 in 10.6 (osx86)
Wasted on Steam - an analytic tool for the Steam platform
Rails 3.1 — SQL logging to STDOUT during testing (with rspec, test::unit, or cucumber)
Rails 3.1 — Using ERB/HAML/etc within a Coffeescript JS file
Rails 3.1 — 'load_missing_contant': Expected ... to define ... (LoadError)
View the entire archive of articles