isotoma / linkedin-cognito-openid-wrapper

BSD 3-Clause "New" or "Revised" License
4 stars 1 forks source link

GitHub OpenID Connect Wrapper for Cognito

Do you want to add GitHub as an OIDC (OpenID Connect) provider to an AWS Cognito User Pool? Have you run in to trouble because GitHub only provides OAuth2.0 endpoints, and doesn't support OpenID Connect?

This project allows you to wrap your GitHub OAuth App in an OpenID Connect layer, allowing you to use it with AWS Cognito.

Here are some questions you may immediately have:

Project overview

When deployed, this project sits between Cognito and GitHub:

Overview

This allows you to use GitHub as an OpenID Identity Provider (IdP) for federation with a Cognito User Pool.

The project implements everything needed by the OIDC User Pool IdP authentication flow used by Cognito.

It implements the following endpoints from the OpenID Connect Core Spec:

It also implements the following OpenID Connect Discovery endpoint:

Out of the box, you can deploy it as a CloudFormation stack, or run it as a web server with node.

Getting Started

This project is intended to be deployed as a series of lambda functions alongside an API Gateway. This means it's easy to use in conjunction with Cognito, and should be cheap to host and run.

You can also deploy it as a http server running as a node app. This is useful for testing, exposing it to Cognito using something like ngrok.

1: Setup

You will need to:

(If you use GitHub Enterprise, you need the API & Login URL. This is usually https://<GitHub Enterprise Host>/api/v3 and https://<GitHub Enterprise Host>.)

Next you need to decide if you'd like to deploy with lambda/API Gateway (follow Step 2a), or as a node server (follow Step 2b)

2a: Deployment with lambda and API Gateway

2b: Running the node server

  source config.sh

3: Finalise Cognito configuration

Attribute mapping

That's it! If you need to redeploy the lambda/API gateway solution, all you need to do is run npm run deploy again.

Logging

This shim also supports logging with Winston. By default, all logging goes to STDOUT. Beware that if you set the log level to DEBUG, then sensitive user information may be logged.

If you're using the node server, you can also use Splunk for logging. Environment variables configuring splunk are commented in example-config.sh. The Splunk HEC URL and access token are required, and you can also set the source, sourcetype & index for all logged events.

The details

Background

There are two important concepts for identity federation:

OAuth

OAuth2.0 is an authorisation framework, used for determining whether a user is allowed to access a resource (like private user profile data). In order to do this, it's usually necessary for authentication of the user to happen before authorisation.

This means that most OAuth2.0 implementations (including GitHub) include authentication in a step of the authorisation process. For all practical purposes, most OAuth2.0 implementations (including GitHub)can be thought of as providing both authorisation and authentication.

Below is a diagram of the authentication code flow for OAuth:

OAuth flow

(The solid lines are http requests from the browser, and then dashed lines are back-channel requests).

As you can see in the diagram, a drawback of OAuth is that it provides no standard way of finding out user data such as name, avatar picture, email address(es), etc. This is one of the problems that is solved by OpenID.

OpenID Connect

To provide a standard way of learning about users, OpenID Connect is an identity layer built on top of OAuth2.0. It extends the token endpoint from OAuth to include an ID Token alongside the access token, and provides a userinfo endpoint, where information describing the authenticated user can be accessed.

OpenID Connect

OpenID Connect describes a standard way to get user data, and is therefore a good choice for identity federation.

A custom shim for GitHub

This project provides the OpenID shim to wrap GitHub's OAuth implementation, by combining the two diagrams:

GitHub Shim

The userinfo request is handled by joining two GitHub API requests: /user and /user/emails.

You can compare this workflow to the documented Cognito workflow here

Code layout

├── scripts             # Bash scripts for deployment and key generation
├── src                 # Source code
│    ├── __mocks__      # Mock private key data for tests
│    └── connectors     # Common code for both lambda and web handlers
│         ├── lambda    # AWS lambda handlers
│         │    └── util # Helper functions for lambdas
│         └── web       # Express.js webserver (useful for local deployment)
├── docs                # Documentation images
├── config              # Configuration for tests
├── dist-web            # Dist folder for web server deployment
└-- dist-lambda         # Dist folder for lambda deployment

npm targets

Scripts

Tests

Tests are provided with Jest using chai's expect, included by a shim based on this blog post.

Private key

The private key used to make ID tokens is stored in ./jwtRS256.key once scripts/create-key.sh is run (either manually, or as part of npm install). You may optionally replace it with your own key - if you do this, you will need to redeploy.

Missing features

This is a near-complete implementation of OpenID Connect Core. However, since the focus was on enabling Cognito's authentication flow, you may run in to some missing features if you wish to use it with a different client.

Missing Connect Core Features:

If you don't know what these things are, you are probably ok to use this project.

Missing non-core features:

A full OpenID implementation would also include:

Known issues

See the issue tracker for an up to date list.

Extending

This section contains pointers if you would like to extend this shim.

Using other OAuth providers

If you want to use a provider other than GitHub, you'll need to change the contents of userinfo in src/openid.js.

Using a custom GitHub location

If you're using an on-site GitHub install, you will need to change the API endpoints used when the github object is initialised.

Including additional user information

If you want to include custom claims based on other GitHub data, you can extend userinfo in src/openid.js. You may need to add extra API client calls in src/github.js

Contributing

Contributions are welcome, especially for the missing features! Pull requests and issues are very welcome.

FAQ

How do I use this to implement Cognito logins in my app?

Login requests from your app go directly to Cognito, rather than this shim. This is because the shim sits only between Cognito and GitHub, not between your app and GitHub. See the Cognito app integration instructions for more details.

Can I use this shim to connect to GitHub directly from another OpenID client?

Yes. This implementation isn't complete, as it focusses exclusively on Cognito's requirements. However, it does follow the OpenID spec, and is complete enough to be able to use it as an OpenID connect provider. See the missing features section above for one or two caveats.

How do I contact you to tell you that I built something cool with this code?

If you build anything cool, ping me @JonesTim on twitter (or open an issue if you have any problems).

License

BSD 3-Clause License