Building my first Github App
I recently built a GitHub Application for Stickler CI and wanted to share what I learned along the way. While the documentation for GitHub Applications is pretty good there were a few things I struggled with.
What is a GitHub App, why would I want to build one?
In addition to OAuth integrations which have existed for almost a decade, GitHub now offers GitHub Applications. OAuth integrations allow an integration to request coarsely scoped access to a user’s account, and act as that user. For example, an OAuth integration cannot ask for write access to a few repositories. Instead it must ask for write access on all public and/or private repositories. This can make it harder to attract users as they don’t want to give you carte blanche with their data. If an OAuth application wants to act as a separate identity, then an additional GitHub account is required.
GitHub Applications by contrast can be ‘installed’ at the organization level, and only be given access to specific repositories. Furthermore, they can request more fine-grained permission controls at the repository level. For example, an application can request write access to statuses but only read access to the code. The end result of these finer grained permissions means that installing an integration for an open source project doesn’t expose your day job’s code. Once ‘installed’ an application acts on its own behalf, and doesn’t ever need to pretend to be a normal human. This removes the need to have a separate ‘bot’ account in GitHub.
More complex data model
GitHub Applications offer several benefits but they come with some drawbacks. The first is a far more complex suite of authentication mechanisms. The new conceptual model is that Applications are added to organizations creating ‘installations’. Installations gain access to an organizations repositories, and other organization resources.
Three authentication modes
The more complex model of GitHub Applications requires more authentication methods. In total GitHub Applications require three kinds of authentication credentials.
1. Private key signed JWT tokens. This authentication mode is required when acting as the ‘application’. You are acting as the application when you want to enquire about your installations, or interact with a specific installation.
2. Installation Token. Installation tokens are created using a JWT. Once you have an installation token you can access the repositories enabled for that installation.
3. OAuth Token – Applications can use OAuth to identify users. I personally didn’t end up using this mode as you can’t request additional scopes which makes it impossible to read a users organizations.
The Installation and JWT based APIs require you to set an Accept
header of application/vnd.github.machine-man-preview+json
. However, once you have an installation access token you don’t need that header.
Knowing which authentication mode you have to use for which APIs requires reading the documentation carefully, as the required authentication mode is not highlighted. Any API endpoint that isn’t explicit about which kind of authentication it requires likely requires an installation token. Not all APIs are usable by GitHub Applications, so keep an eye out for the blue ‘i’ indicating an endpoint can be accessed via an installation token. If there is no ‘i’ that endpoint cannot be used by a GitHub Application.
Another wrinkle with GitHub Applications is how long tokens live for. JWT tokens live for a maximum of 10 minutes, while installation tokens live for 1 hour. This is very different than OAuth tokens, which live forever. The shorter token lifetime meant that I needed to ensure I created a new installation token when starting a review job, and that each web request created a new JWT token.
Clunky OAuth mode
I found the OAuth mode of a GitHub Application to be awkward. It doesn’t allow you to request additional scopes and can only be used to identify a user. Because of this, I wasn’t able to get a users’ organization list which made building the UI flow I wanted impossible.
I ended up needing to use both an OAuth integration and a GitHub Application. The OAuth integration allows me to access a user’s profile and organization data. With that I can use the Application JWT to discover a user’s installations. This combination allowed me to display organizations with and without Application installs. This approach was not documented, so I suspect it is not a recommended approach. However, it made migrating to a GitHub application easier, as my existing users can opt-in to installing the GitHub Application without disrupting their existing OAuth integration.
Once authentication modes were solved I had to change very little about my integration. One important thing I did have to change was how repositories are cloned. With an OAuth application a clone URL looks like:
https://<token-value>:x-oauth-basic@github.com/user/repo.git
While with a GitHub Application you must use.
https://x-access-token:<token-value>@github.com/user/repo.git
In closing
I found the process of implementing a GitHub Application straight forward once I grokked the new data model and authentication complexity. I’m glad to see GitHub making significant investments in their integration ecosystem that make it easier and better for users to adopt integrations, and offer new features like Checks.
There are no comments, be the first!