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 your.web.app/callback.
2. Redirect users to request GitHub access
Assuming the environment variables CLIENT_ID and CLIENT_SECRET exist…
$ open https://github.com/login/oauth/authorize?client_id=${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'
@app.route('/callback')
def callback():
    if 'code' in request.args:
        url = 'https://github.com/login/oauth/access_token'
        payload = {
            'client_id': environ.get('CLIENT_ID'),
            'client_secret': environ.get('CLIENT_SECRET'),
            'code': request.args['code']
        }
        headers = {'Accept': 'application/json'}
        r = requests.post(url, 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']
        else:
            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).
@app.route('/')
def index():
    # authenticated?
    if not 'access_token' in session:
        return 'Never trust strangers', 404
    # get username from github api
    url = 'https://api.github.com/user?access_token={}'
    r = requests.get(url.format(session['access_token']))
    try:
        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
| Previous: | Jenkins and Nginx |