In our latest rewrite of edgɘyo, we made a decision early in the implementation phase to rely on OAuth providers for login and authentication. Given that this is the 7th re-implementation of the same software, I’ve written/integrated 6 authentication already, and honestly speaking I’m quite tired of reimplementing the wheel.
The gist is this – if you are operating a web service/app that is not seeking to create a brand new market or take over the world, integration should be the key to your app’s success.
I would argue that Instagram would not have succeeded had it not integrated with Facebook; or that smartphones would be pointless had they not integrated with Twitter, Facebook, and other social services. Most users begin their internet life through a social service nowadays. It’s their window to the rest of the internet world, and to re-implement the whole authentication system would be foolish if your webapp is not an app that seeks to do that (as in, being the fulcrum of your user’s entire internet existence).
As of now, we have integrated with Twitter, Facebook, Angel List, Github and Linked In, and planning to add in Google, Dropbox and Tumblr.
This of course, creates an ecosystem issue where with so many providers to choose from, our users might forget what provider they initially logged in from.
Organizing the OAuth spaghetti
Our current solution is a 3 step process which we hope to mitigate this problem to an extend:
- Make users create a username and provide an e-mail address upon signing-up through any provider
- The system takes the username->email combo to check if there’s already another such combo registered with another provider
- If found, the user has the option to send a “Join Account” link to that user and e-mail address
At this point the user can log in again using the original provider, and he will see a notification on his dashboard asking to join the accounts. Upon authorizing it, the user will now be able to log in via either accounts.
Having said that, obviously our technical implementation is far more complex than just that.
OAuth is ridiculously easy
The real point of this article is that I personally believe OAuth should be part of every web developer’s toolkit. Implement a pet project using OAuth, try it out, deploy it, and know how to play around with the protocol. OAuth is the future, there’s no doubt of it.
On my side, I absolutely love everyauth. It just works™. We implemented all our current OAuth providers easily in less than 2 hours, with the majority of time spent setting up app accounts in the respective providers rather than writing code.
Here’s how our everyauth code looks like:
'use strict';
module.exports.make = function make(deps) {
if (undefined === deps) throw new Error('No dependency injected');
var auth = {
callbackError : function callbackError(req, res) {
res.redirect('/error/401');
},
touchUser : function touchUser(session, accessToken, accessTokExtra, metadata) {
var db;
db = deps.db;
return { user : 'soggie' };
},
init : function init() {
var eauth, config;
eauth = deps.eauth;
config = deps.config;
// Setup github oauth
eauth.github
.appId(config.github.appId)
.appSecret(config.github.appSecret)
.handleAuthCallbackError(auth.callbackError)
.findOrCreateUser(auth.touchUser)
.redirectPath('/dashboard')
.scope('user');
// Setup linkedIn oauth
eauth.linkedin
.consumerKey(config.linkedIn.consumerKey)
.consumerSecret(config.linkedIn.consumerSecret)
.handleAuthCallbackError(auth.callbackError)
.findOrCreateUser(auth.touchUser)
.redirectPath('/dashboard');
// Setup twitter oauth
eauth.twitter
.consumerKey(config.twitter.consumerKey)
.consumerSecret(config.twitter.consumerSecret)
.handleAuthCallbackError(auth.callbackError)
.findOrCreateUser(auth.touchUser)
.redirectPath('/dashboard');
// Setup facebook oauth
eauth.facebook
.appId(config.facebook.appId)
.appSecret(config.facebook.appSecret)
.handleAuthCallbackError(auth.callbackError)
.findOrCreateUser(auth.touchUser)
.redirectPath('/dashboard');
// Setup angellist oauth
eauth.angellist
.appId(config.angellist.appId)
.appSecret(config.angellist.appSecret)
.handleAuthCallbackError(auth.callbackError)
.findOrCreateUser(auth.touchUser)
.redirectPath('/dashboard');
}
}
return auth;
}
Ridiculously simple eh? Sorry that I removed all line spacings in that code snippet. My markdown plugin doesn’t play nice with my syntax highlighter.
Anyway, much love to OAuth, and the node community. Signing out now!
PS: I noticed that most of the services I mentioned are all OAuth 2. The “OAuth” termed here is an umbrella term I use to refer to all OAuth (both 1 and 2) implementations in general.
PSPS: I blogged further about 5 Reasons Why Startups Should Use OAuth in the edgɘyo blog too.
[...] recently wrote in my personal blog about the technical details of implementing OAuth in edgɘyo, and I think the technical decision to go OAuth rather than rolling our own [...]