OAuth Like a BOSS
Tags: oauth, node, javascript, authentication
Published: 2019-01-07 12:16:05

---
title: OAuth Like a BOSS
published: true
description: Once And For All
tags: oauth, node, javascript, authentication
---

In my [previous article] I talked about **[Grant]**: 

> An OAuth middleware for NodeJS that also happens to be a completely transparent OAuth proxy.

This time around we're going to explore a few real world examples: 

0. Login from a server app, written in JavaScript.
1. Login from a browser app hosted on GitHub Pages.
2. Login from a Browser Extension.
3. Login from a server app, written in another programming language.

# The Usual Stuff

Imagine that we have a web app hosted on `awesome.com` Imagine also that our app leverages the [GitHub API] to manage our user's repositories. We also have a web form on our website to allow our users to pick only the [permissions] they are willing to grant to our app:

```html
<form action="/connect/github" method="POST">
  <p>Grant read/write access to:</p>
  <label>
    <input type="radio" group="scope" name="scope" value="repo" />
    public and private repositories</label>
  <label>
    <input type="radio" group="scope" name="scope" value="public_repo" />
    public repositories only</label>
  <button>Login</button>
</form>
```

This form is going to `POST` the chosen OAuth `scope` to the `/connect/github` route, that [Grant] operates on.

Next we need a [Grant] server to handle the OAuth flow for us:

```js
var express = require('express')
var session = require('express-session')
var parser = require('body-parser')
var grant = require('grant-express')

express()
  .use(session({secret: 'dev.to'}))
  .use(parser.urlencoded()) // only needed for POST requests
  .use(grant(require('./config.json')))
  .use('/login', (req, res) => res.end(`the above HTML form`))
  .use('/hello', (req, res) => {
    var {access_token} = req.session.grant.response
    console.log(access_token)
    res.end('nice!')
  })
  .listen(3000)
```

With the following configuration:

```json
{
  "defaults": {
    "origin": "https://awesome.com", "state": true, "transport": "session"
  },
  "github": {
    "key": "...", "secret": "...", "dynamic": ["scope"], "callback": "/hello"
  }
}
```

We are allowing the OAuth `scope` to be set `dynamic`ally for GitHub. We are also going to use the session as `transport` to deliver the result of the OAuth flow in our final `callback` route.

Lastly, we have to create an actual [OAuth app] on GitHub and copy/paste its `key` and `secret` to the configuration above. We also have to set its _Authorization callback URL_ to `https://awesome.com/connect/github/callback`, that's the second route reserved by [Grant].

This will allow us to pick a scope and login with GitHub by navigating to `https://awesome.com/login`

# Login from Another Host

Now imagine that we have another app hosted on [GitHub Pages] at `https://simov.github.io/stars/`, that allows users to explore stats and history about the Stars received by a given repository hosted on GitHub.

Our app is going to access public data only. Unfortunately the default rate limit imposed by GitHub on their API is 60 HTTP request per hour. If the request is sent along with an access token, however, the rate limit is lifted to up to 5000 HTTP requests per hour.

So, we need to login again, **but** we already have a [Grant] server up and running on `awesome.com`, so why not reuse it:

```json
{
  "defaults": {
    "origin": "https://awesome.com", "state": true, "transport": "session"
  },
  "github": {
    "key": "...", "secret": "...", "dynamic": ["scope"], "callback": "/hello",
    "overrides": {
      "stars": {
        "key": "...", "secret": "...", "dynamic": ["callback"], "transport": "querystring"
      }
    }
  }
}
```

We want to have a sub configuration for GitHub, called `stars`. It will be a different [OAuth app], notice the `key` and the `secret`. 

We also want to set the final `callback` URL `dynamic`ally, but not the `scope` allowed above it. We're going to login without any explicit scopes set, which [in the case of GitHub] means getting read access to public data only.

Lastly, we're overriding the `transport` inherited from the `defaults`. We need the response data encoded as querystring in a fully qualified absolute `callback` URL, pointing back to our browser app hosted on [GitHub Pages].

Then we have to navigate to the connect route to login:

```js
// store the current URL
localStorage.setItem('redirect', location.href)
// set callback URL dynamically - https://simov.github.io/stars/
var callback = encodeURIComponent(location.origin + location.pathname)
// navigate to the connect route
location.href = `https://awesome.com/connect/github/stars?callback=${callback}`
```

It's handy to have our final `callback` URL set dynamically in case we want to host our app on a different domain in future.

After logging in, the user will be redirected back to our browser app, hosted on GitHub:

```
https://simov.github.io/stars/?access_token=...
```

Now is the time to extract the `access_token` from the querystring and store it for future use:

```js
var qs = new URLSearchParams(location.search)
localStorage.setItem('token', qs.get('access_token'))
```

It's a nice final touch to redirect our users back to where they were before embarking on the quest to login with our OAuth app:

```js
location.href = localStorage.getItem('redirect') // go back to the last URL
localStorage.removeItem('redirect')
```

Here is [the app] I was talking about.

# Login from Browser Extension

Next we have a [Browser Extension] that augments the GitHub's UI by adding a cool little button to it. When clicked, it aggregates some useful information about the repository we are currently browsing:

```json
{
  "manifest_version": 2,
  "name": "Cool Little Button",
  "version": "1.0.0",
  "background" : {"scripts": ["background.js"]},
  "content_scripts": [{"matches": ["https://github.com/*"], "js": ["content.js"]}],
  "permissions": ["storage"]
}
```

This extension, however, relies on data fetched from the [GitHub API], that by default will, again, limit us to 60 HTTP requests per hour. So it will be nice to let our users login quickly and easily, directly from within our extension, and therefore lift the rate limit to up to 5000 HTTP requests per hour:

```json
{
  "defaults": {
    "origin": "https://awesome.com", "state": true, "transport": "session"
  },
  "github": {
    "key": "...", "secret": "...", "dynamic": ["scope"], "callback": "/hello",
    "overrides": {
      "stars": {
        "key": "...", "secret": "...", "dynamic": ["callback"], "transport": "querystring"
      },
      "extension": {
        "dynamic": false, "transport": "querystring",
        "callback": "https://github.com/extension/callback"
      }
    }
  }
}
```

This time we are going to reuse the OAuth app inherited from the root level of the GitHub configuration (the `key` and the `secret`). We also don't want the user to set any of the configuration options `dynamic`ally.

Then we can open up a new tab from within the `background.js` script and let our users login:

```js
chrome.tabs.create({url: 'https://awesome.com/connect/github/extension')})
```

We will redirect our users back to a non existing page on GitHub. In this case GitHub will respond with generic HTML page for the 404 Not Found status code, **but** our access token will be encoded in the querystring:

```
https://github.com/extension/callback?access_token=...
```

So again, all we need to do is extract it and store it, this time by placing the following code into the `content.js` script:

```js
var qs = new URLSearchParams(location.search)
chrome.storage.sync.set({token: qs.get('access_token')})
```

# Login from Another Language

[Grant] is not limiting us to JavaScript and NodeJS on the server. Being able to be configured dynamically over HTTP, and sent back the result wherever we want to, Grant gives us access to **180+** login providers from **any** other programming language.

This time around we'll name our sub configuration `proxy`, allowing `dynamic` configuration of every option available in Grant:

```json
{
  "defaults": {
    "origin": "https://awesome.com", "state": true, "transport": "session"
  },
  "github": {
    "key": "...", "secret": "...", "dynamic": ["scope"], "callback": "/hello",
    "overrides": {
      "stars": {
        "key": "...", "secret": "...", "dynamic": ["callback"], "transport": "querystring"
      },
      "extension": {
        "dynamic": false, "transport": "querystring",
        "callback": "https://github.com/extension/callback"
      },
      "proxy": {
        "dynamic": true
      }
    }
  }
}
```

For the purpose of this example I'm going to use [Go], I just had to pick one, but the following applies to any other language:

```go
package main
import (
  "fmt"
  "net/url"
  "net/http"
)
func main() {
  http.HandleFunc("/login", func (w http.ResponseWriter, r *http.Request) {
    qs := url.Values{}
    qs.Add("key", "...") // yes
    qs.Add("secret", "...") // we're passing an OAuth app dynamically!
    qs.Add("scope", "repo user gist")
    qs.Add("transport", "querystring")
    qs.Add("callback", "http://localhost:3000/hello")
    http.Redirect(w, r, "https://awesome.com/connect/github/proxy?" + qs.Encode(), 301)
  })
  http.HandleFunc("/hello", func (w http.ResponseWriter, r *http.Request) {
    qs, _ := url.ParseQuery(r.URL.RawQuery)
    fmt.Println(qs["access_token"][0])
    w.Write([]byte("nice!"))
  })
  http.ListenAndServe(":3000", nil)
}
```

Now all we need to do is navigate to `http://localhost:3000/login`

# Conclusion

Well, I can summarize this entire article in 5 lines of JSON configuration.

The point of it, however, was to demonstrate how the server side login can be connected with various types of applications, and how [Grant] fits in the broader picture.

Hopefully this will serve as a handy guide for frontend developers wanting the leverage the servers side login, and also backend developers wanting to quickly get access to a lot of providers.

Here is [the source code] of all examples.

Happy Coding!


  [previous article]: https://dev.to/simov/oauth-simplified-2pbd
  [in the case of GitHub]: https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/#available-scopes
  [permissions]: https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/#available-scopes
  [Grant]: https://github.com/simov/grant
  [OAuth app]: https://github.com/settings/applications/new
  [GitHub API]: https://developer.github.com/v3/
  [GitHub Pages]: https://pages.github.com/
  [Browser Extension]: https://developer.chrome.com/extensions
  [the app]: https://github.com/simov/stars
  [Go]: https://golang.org/
  [the source code]: https://github.com/simov/oauth-like-a-boss