loading...

OAuth Simplified

simov profile image simo Updated on ・3 min read

We often need to implement some sort of login functionality in our web apps, or authorize our app to access the user's data programmatically.

Historically this was done by using the user's password directly for authorization. Unfortunately this leads to alot of problems, so a delegated authorization framework was invented, called OAuth.

Two versions of OAuth are available: OAuth 1.0a and OAuth 2.0, the latter being the most widely used today. Both OAuth 1.0a and the so called server side flow (Authorization Code Grant) of OAuth 2.0 require a server app, and involve multiple steps to improve security.

This makes these two types of authorization flows a bit more complicated to implement, so naturally, we look for third-party modules ready to drop in and get going.

Login

A few years ago I was using Passport to implement login functionality using OAuth in my NodeJS app. The problem was, however, that Passport was never built around the idea of having multiple login providers in a single app. While totally possible it required separate module for every provider, developed by a different person.

On top of that Passport makes one additional request after the OAuth flow to get a more detailed profile of the user. Unfortunately that's not part of the specification, and over time it become an obstacle, because I always needed something slightly different.

I ended up with fair amount of glue code in my app, just for a handful of login providers. And to make matters worse - now I had to write tests for it.

So I asked myself:

Is it possible to create something dramatically simpler?

You see, it doesn't matter if something already exists:

You can innovate always!

You can always take already existing solution and create a better one, a simpler one, unique in its own way.

Meet Grant!

Goals

Grant was built around very narrow and extreme use case:

Support for unlimited number of OAuth providers, without the need of any glue code.

As we all know good configuration data structure is the backbone of every great app. At its core Grant is exactly that:

A simple, yet powerful configuration data structure.

Having all aspects of the module defined as JSON, opens up the doors for all kinds of interesting use cases:

  • static configuration per environment
  • nested static sub configurations per provider
  • dynamic configuration per authorization attempt

In fact, the configuration is not even required, it can be passed dynamically via GET or POST request:

Making Grant a completely transparent OAuth proxy.

Meaning that you can deploy it somewhere on your stack and access it from any other server and programming language.

How

First we need to register an OAuth app for every provider that we want to login with. For example, we have to register OAuth 2.0 client app for Google, and OAuth 1.0a client app for Twitter. The redirect URL of our OAuth apps should always end with /connect/[provider]/callback:

http://localhost:3000/connect/google/callback
http://localhost:3000/connect/twitter/callback

Then we need a configuration file where we can put our OAuth app credentials along with a few other options:

{
  "defaults": {"origin": "http://localhost:3000", "callback": "/hello", "state": true},
  "google": {"key": "...", "secret": "...", "scope": ["openid"], "nonce": true},
  "twitter": {"key": "...", "secret": "..."}
}

Lastly, our server may look like this:

var express = require('express')
var session = require('express-session')
var grant = require('grant-express')

express()
  .use(session({secret: 'dev.to'}))
  .use(grant(require('./config.json')))
  .use('/hello', (req, res) => res.end(JSON.stringify(req.query, null, 2)))
  .listen(3000)

This will allow us to login with Google and Twitter by navigating to the following URLs in our browser:

http://localhost:3000/connect/google
http://localhost:3000/connect/twitter

Conclusion

OAuth has never been easier!

And to prove that Grant comes with support for 180+ login providers, but any other provider conforming to the OAuth spec should work out of the box.

Grant was also developed along with an example app showcasing the design goals behind it, and its real potential. As you can imagine this app consists of almost no code on the server, and only a JSON configuration.

Lastly, the official documentation of the module is a great source of information where I try to cover diverse set of features and use cases.

Happy Coding!

Discussion

pic
Editor guide
Collapse
thepassle profile image
Pascal Schilp

This looks very promising, adding authentication to my side projects is always the biggest hurdle and a lot of the passport options seem outdated. Hopefully this is as plug and play as it looks.

Is there a source code for the example app that you posted? I saw a couple of demos in the repo, but i didnt specifically see the source code for the example app you posted. (this one: grant.outofindex.com)

Thanks for sharing!

Collapse
simov profile image
simo Author

Hi, Pascal, the actual source code of the example app isn't available yet, but there is really not much into it. Actually the info on the index page of the app (grant.outofindex.com) outlines just about everything.

Here is the example you are looking for: github.com/simov/grant/tree/master...

If you inspect the example app you'll see that I have exactly that - an HTML form that POST's to the /connect/[provider] route.

Also you may find this comment useful: github.com/simov/grant/issues/61#i...

Hope that helps.

Collapse
thepassle profile image
Pascal Schilp

The reason I asked is because that issue is from back in 2016, and the explanation at grant.outofindex.com links to github issues and bits of documentation. I would've loved to see the source code of the example app to see it all pieced together. Any chance you could still share it somehow?

Thread Thread
simov profile image
simo Author

My stack consists of NginX and NodeJS+Express on the server, and Mithril in the browser. I also have separate scripts for building the config, as you can imagine there is a lot of configuration for 180 providers with all their scopes, custom fields and so on. It's fairly specific to my taste and needs, and while the code is utterly simple it was never meant to be shared as something like an 'app' that people can look and learn from.

Also what if you want to see a browser side implementation with React? Well, no luck then, I don't have any. And while the info may be a bit scattered across a few examples and sections of the docs - it's actually a fairly trivial task to implement for any web developer.

If you have any specific questions, I'm always ready to help!

Thread Thread
simov profile image
simo Author

Hi, @thepassle , I just published my new article about Grant:

Hopefully this answers a question or two.

Thread Thread
thepassle profile image
Pascal Schilp

Hey! Thanks for getting back to me, i'll be sure to check it out

Collapse
harrisonkugler profile image
Harrison Kugler 🚀🏄

Looks awesome, I am currently building a PWA with sails.js and have been looking into using passport, but this seems like a much more straightforward solution.

Collapse
graxmonzo profile image
Bekzat Samatov

Hey! How is it going?

Collapse
sebastiandg7 profile image
Sebastián Duque G

Hi. I'm currently getting the thing about OAuth/2. Grant purpose is to easily setup OAuth authentication with OAuth2 providers, such as Google, Twitter, Facebook, OpenID... Etc. However, what if I want my app itself to be an OAuth2 provider? Does Grant help with this?

Collapse
simov profile image
simo Author

Hi, Sebastian, Grant is an OAuth Client. Have a look at NPM, there should be at least a few good provider implementations.

Collapse
shixilun profile image
shixilun

Too bad, I had been hoping for a simplified explanation of OAuth. You still have to send a password for authorization, so I don't see how OAuth is an improvement over that.

Collapse
simov profile image
simo Author

When website owners use OAuth for authentication they don't have to deal with storing the user's credentials in their own database. They simply redirect the user to the OAuth provider's website - the password is entered only once and only there.

When app developers use OAuth for perpetual authorization they only send an access token with each and every request, but not the actual password of the user.

In both cases the third-party never knows the actual password of the user.