marc walter

Elm Oauth integration (1 of 2 - GitHub)

2018-06-16 (Last updated on 2018-09-07)

Example app animation

Background

I am working on a web service that a user may access without registration, but where she may invite other people via email.
To mitigate misuse of the service for spam, I decided to require a validated email address before the user may invite someone else. One simple way is to send a registration email to the user to activate the feature.
But I also want the possibility to add the email feature without requiring the user to wait for an email.
To achieve this, I decided to use OAuth 2.0 integration, and first use GitHub.

TLDR: Example code written in JavaScript with koa for the server and in elm for the client is available for download and on github.

GitHub integration

Note: This follows the official documentation on authorizing OAuth apps.

General OAuth 2.0 flow

  1. Generate a GitHub authentication URL
  2. Redirect the user to GitHub
  3. User enters her credentials to confirms provide her identity
  4. User is redirected back to your site by GitHub
  5. Rehydrate the web app using the returned information
  6. Generate an access token for the GitHub API
  7. Retrieve the email address

Register an OAuth app

First, register a new OAuth application and specify the authorization callback URL. This is the URL that GitHub returns the user to after authentication.

Because I develop locally and my server runs at http://localhost:3000, I chose http://localhost:4567/oauth to allow room for further OAuth integrations.

After that, copy the generated Client ID and Client Secret.

1. Building a GitHub authentication URL

I only want the user's email address, so I limit my scope to user:email. All available scopes are documented on github.

Building the URL: https://github.com/login/oauth/authorize?client_id=<github_client_id>&scope=user:email&state=<custom_data>

2. Redirecting the user to GitHub

Change the page.

3. Authentication on GitHub

User enters her credentials if she is not logged in already and approves access to her data.

4. GitHub redirects the user to the web app

Github passes a user code and the state passed into the authentication (which may be used for CSRF protection or exchange of app state) as query parameters e.g. /auth/github/?code=<code>&state=<state>.

5. Rehydrate the web app

Either the server parses the query parameters to fetch the users current state from a database, or the web app could use the state to inject it again.
The latter is not an option for me, because the state could contain personal data that should not be shared to an arbitrary service in the US of A.

6. Create a GitHub API access token

Must be done on the server, you need the client id and the client secret of the OAuth app.
You need to POST to https://github.com/login/oauth/access_token?client_id=<client_id>&client_secret=<client_secret>&code=<code>&state=<state>. You may specify a data format in the HTTP Accept header, I used application/json.

7. Retrieve the email address

After the server receives the access token, it can GET https://api.github.com/user to retrieve the user information including the email address. I set the HTTP headers Accept to application/json and Authorization to token <access_token>

Overview

Server

HTTP Route Description
GET /index Serves the web app
POST /auth/github/get-url Receives the web app state and returns the redirect URL
GET /auth/github/ Serves the web app with saved web app state
POST /auth/github/ Receives the user code, communicates with GitHub, and returns the user's email address

Important bits

In server.js:

  • When generating the GitHub authentication URL, I also added a redirect_url, which contained the database key of the current transaction, this allows me to retrieve the user's state. It is not necessary here, but for instance google auth does not use query parameters, but appends it after a '#', which is not sent to the server.

In elm/Main.elm:

  • The init function
    • When the user opens the index page, receives an empty JSON value and tries to decode it. If decoding is not successful, default data is used.
    • When the user enters from /auth/github/, receives the question and (partial) answer from the server and displays it
    • When the user returns from GitHub, reads the user code and state from the URL and initiates retrieval of the GitHub email address
  • While the server queries GitHub for the email address, the user can continue working with the web app. The email address will be added when it arrives.

Download

The code written in JavaScript using koa for the server and in elm for the client is available for download and on github.