Adding Google OAuth 2.0 with PassportJS

PassportJS makes authentication a breeze! Here’s a quick run down for a typical express app.

For federated authentication, OAuth 2.0 is taking traction and is the recommended protocol. You can find the google oauth strategy here.

Install

Protip: Use –save to automagically update your package.json

$ npm install passport --save
$ npm install passport-google-oauth --save

Configure Strategy

In your app, require the above modules

var passport = require('passport')
  , GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

Then request a client ID from Google APIs for OAuth2. Hang onto the client id, client secret, and redirect URI. I set up my redirect url as exampledomain.com/auth/google/callback. Make sure you keep it consistent throughout your passport configurations as well as the google api console.

Next, configure your google strategy:

passport.use(new GoogleStrategy({
    clientID: YOUR_CLIENT_ID,
    clientSecret: YOUR_CLIENT_SECRET,
    callbackURL: app.get('site url') + '/auth/google/callback',
    scope: 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.email 
      https://www.googleapis.com/auth/userinfo.profile'
  },
  function(accessToken, refreshToken, profile, done) {
     User.updateOrCreate({ googleId: profile.id }, 
        function(err, user) {
          if(err) { throw err; }
            done(null, user);
          }
    );
  }
));

Note: It’s a good idea to map your site url to development or production environments. The express way of doing this is through app.set() and app.set().

// NODE_ENV == 'production' 
app.configure('production', function() {
  app.set('site url', env.site_url.prod);       // www.domain.com
});

// NODE_ENV == 'development'
app.configure('development', function() {
  app.set('site url', env.site_url.dev);        // localhost:3000
});

Session Serialization

For sessions, passport maintains a cookie that identifies each session. Each request doesn’t contain credentials but just the unique identifier for that session. Basically, the client hangs onto its ID while the server deserializes the id to the User object. This object is then accessed via req.user.

The done() function is known as the verify callback which is called after you check for valid credentials. In our case, we’re using OAuth so we’re checking to see if the id matches someone in our database. In basic authentication, you would check if username and password match and THEN use done().

passport.serializeUser(function(user, done) {
  done(null, user.id);    // id stored in a delicious cookie
});

passport.deserializeUser(function(id, done) {
  User
    .findOne({ googleId: id })
    .exec(function(err, user) {
      done(err, user);    // Now req.user == user
    });
});

Here’s an example of supporting multiple auth strategies. All you would need to do is configure more strategies like the above google example.

passport.serializeUser(function(user, done) {
  var ids = {
    facebookId: user.facebookId,
    twitterId: user.twitterId,
    googleId: user.googleId
  };
  done(null, ids);    // ids stored in a delicious cookie
});

passport.deserializeUser(function(ids, done) {
  User
    .findOne({ facebookId: ids.facebookId })
    .or({ twitterId: ids.twitterId })
    .or({ googleId: ids.googleId })
    .exec(function(err, user) {
      done(err, user);    // Now req.user == user
    });
});

Define Routes

Last but not least, let’s define our routes for authentication:

app.get('/auth/google', passport.authenticate('google'));
app.get('/auth/google/callback',
  passport.authenticate('google', { successRedirect: '/',
                                    failureRedirect: '/login'}));

app.get('/login', function(req, res) {
  res.render('login');
});

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

Now you can drop a link to /auth/google which will, after successful login, give you req.user.


comments powered by Disqus