Python and GitHub OAuth2 Flow

Tuesday, 14 July 2015

This weekend I had a go at leveraging GitHub’s OAuth2 flow for a Contributor License Agreement web application. GitHub has some great developer documentation on the subject and even a tutorial including sample Ruby web app.

Here are some brief notes on integrating GitHub OAuth flow into a simple Python + Flask app.

1. Prerequisites

Register an application and make a note of the client ID and client secret. Set the callback URL to

2. Redirect users to request GitHub access

Assuming the environment variables CLIENT_ID and CLIENT_SECRET exist…

$ open${CLIENT_ID}&scope=(no scope)

3. GitHub redirects back to your site

GitHub redirects back to your site (via the callback URL) with a temporary code in a code parameter.

from Flask import Flask, redirect, request, session
from os import environ

app = Flask(__name__)
app.config['SECRET_KEY'] = 'Waffling on is really great for these secrets brandy cactus beartrap'

def callback():
    if 'code' in request.args:
        url = ''
        payload = {
            'client_id': environ.get('CLIENT_ID'),
            'client_secret': environ.get('CLIENT_SECRET'),
            'code': request.args['code']
        headers = {'Accept': 'application/json'}
        r =, params=payload, headers=headers)
        response = r.json()
        # get access_token from response and store in session
        if 'access_token' in response:
            session['access_token'] = response['access_token']
            app.logger.error('github didn\'t return an access token, oh dear')
        # send authenticated user where they're supposed to go
        return redirect(url_for('index'))
    return '', 404

4. Use the access token to access the API

The previous snippet should send the user back to the main page. Let’s welcome them (or otherwise).

def index():
    # authenticated?
    if not 'access_token' in session:
        return 'Never trust strangers', 404
    # get username from github api
    url = '{}'
    r = requests.get(url.format(session['access_token']))
        login = r.json()['login']
    except AttributeError:
        app.logger.debug('error getting username from github, whoops')
        return 'I don't know who you are; I should, but regretfully I don't', 500
    return 'Hello {}!'.format(login), 200