Using GitHub as OpenID IdP with AWS Cognito

Today I was researching to make AWS Cognito works with GitHub as an identity provider (IdP). I thought it would be easy... until it wasn't.

The problem is that Cognito only supports integrating third-party authentication system by using SAML or OpenID. SAML is a difficult choice because GitHub only provides SAML SSO (Single Sign-on) support for plans Enterprise+, which is really hard to get for developers. So the only option left is the OpenID, which is an identity layer on top of the OAuth 2.0 protocol.

Googling for a while, they do give developers ways to create and build an OAuth application on top of GitHub account. You will have to create an OAuth application on GitHub first.

  • Set your GitHub OAuth Authorization Callback URL to https://<your_cognito_domain>/oauth2/idpresponse

The OAuth endpoints given in GitHub documentation were:

  • Request method: GET
  • Scopes: user,openid
  • Issuer: https://github.com
  • Authorize endpoint: https://github.com/login/oauth/authorize
  • Token endpoint: https://github.com/login/oauth/access_token
  • User info endpoint: https://api.github.com/user
  • JWKS endpoint: anything, I put in https://github.com/login/oauth/access_token

Seems about right? Right? When filled into Cognito OpenID Connect, it just didn't work. I got an error like this:

Response+from+IdP+token+endpoint+cannot+be+parsed+as+a+JSON.

Looking back at the documentation, the GitHub implementation of OAuth just didn't match my expectations. For example:

  • Requesting data from the token endpoint, it will return the following form: access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&token_type=bearer, which is incorrect. It should be returning a JSON object for OpenID to understand.
  • User info endpoint using some weird authorization scheme: Authorization: token OAUTH-TOKEN. That's weird to me. OpenID will send a Bearer scheme so that's why it won't work.
  • Playing around in Postman, I noticed that in the user info endpoint, GitHub didn't return sub, a very important field for OpenID Connect and Cognito to map the username.

So I decided to make a "proxy" in Python and Flask to handle these request and returning the correct payload for Cognito to understand.

GitHub Token Endpoint Proxy

The idea for this endpoint is to take the form data sent from AWS Cognito, forward it back to GitHub with the header Accept: application/json for GitHub API to return back in JSON form instead of "query" form.

import requests
from flask import request, jsonify

from main import app


@app.route('/oauth/github/access-token', methods=['POST'])
def get_access_token():
    github_request = requests.post(
        url='https://github.com/login/oauth/access_token',
        headers={
            'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
            'Accept': 'application/json'
        },
        data=request.form.to_dict(),
        allow_redirects=False
    )
    response = github_request.json()

    return jsonify(response)

GitHub User Info Endpoint Proxy

With this one, we will do the following things:

  • Get the Bearer token given by Cognito and modify the header to send token authorization scheme to GitHub.
  • Add a sub field into the response for Cognito to map the username.
@app.route('/oauth/github/user-info', methods=['GET'])
def get_user_info():
    github_request = requests.get(
        url='https://api.github.com/user',
        headers={
            'Authorization': 'token ' + request.headers.get('Authorization').split('Bearer ')[1],
            'Accept': 'application/json'
        },
        allow_redirects=False
    )
    response = github_request.json()

    return jsonify({
        **response,
        'sub': response['id']
    })

Deploy these APIs on your server and delete your current OpenID configuration and add a new one with these values:

  • Request method: GET
  • Scopes: user,openid
  • Issuer: https://github.com
  • Authorize endpoint: https://github.com/login/oauth/authorize
  • Token endpoint: https://<your-api-domain>/oauth/github/access-token
  • User info endpoint: https://<your-api-domain>/oauth/github/user-info
  • JWKS endpont: anything, I put in the token endpoint https://<your-api-domain>/oauth/github/access-token

In your App Client settings, remember to config it to:

  • Allowed OAuth Flows: Authorization code grant
  • Allowed OAuth Scopes: tick user, openid

Hope with my tutorial, you can connect your Cognito app with GitHub as an identity provider using OpenID now. Enjoy!