Posting statuses as a github app using node.js
I recently added a github app for happo.io with the sole purpose of posting back statuses to a commit. While much of the documentation for the github API is outstanding, I found myself struggling with generating the auth tokens necessary to call the statuses endpoint. Authentication when using the API on behalf of a user is easy, you have a wide variety of authentication mechanisms to choose from. Authenticating as an app on the other side, leaves you with just one: JSON Web Tokens (JWT).
Before we dive into the details, here’s what I was aiming for:
To generate a JWT, you first have to generate a private key in PEM format. This key will then be used to sign the JWT before finally using it as an Authorization: Bearer <token>
header in the API call. The official github docs gives you an example of how to do this in Ruby, and since I use Node.js for happo.io, I had to convert that bit of code to JavaScript. Luckily, most of what I needed was available in the jsonwebtoken
npm module. I just had to wire it up and pass the right data to it:
const jsonwebtoken = require('jsonwebtoken');const PEM = '<contents of the generated PEM file>';
const ISSUER_ID = 9999999; // replace with the ID of your github appfunction generateJwtToken() {
return jsonwebtoken.sign(
{
iat: Math.floor(new Date() / 1000),
exp: Math.floor(new Date() / 1000) + 60,
iss: ISSUER_ID,
},
PEM,
{ algorithm: 'RS256' },
);
}
The JWT won’t allow us to use the statuses
endpoint however. To do that, we need to get another auth token, this time tied to an installation of our github app in another organization. For instance, let’s say the “facebook” organization installed the happo github app. In order to post statuses on a commit in the facebook organization, we would have to create a (temporary) auth token using the JWT that we just generated, specific to that installation. We do this by calling an API endpoint on github’s sideWith the help of the octokit/rest
npm module, it will look something like this:
const octokit = require('@octokit/rest');const octokitClient = octokit();
octokitClient.authenticate({
type: 'integration',
token: generateJwtToken(),
}); const { data: { token } } = await octokitClient.apps.createInstallationToken({
installation_id: installationId,
});
octokitClient.authenticate({ type: 'token', token });
The installationId
listed above is the ID github assigns to an organization installing the github app. There are two ways to access this, either through collecting it in a github webhook or by using the installations
API endpoint.
Once you have the auth token for an installation we can finally make that API call we came for in the first place, posting a status. With octoclient
, it looks like this:
await octokitClient.repos.createStatus({
owner,
repo,
sha: 'fcd234cdaa123',
state: 'success',
context: 'Happo',
target_url: triggeredByUrl,
description: 'All good from over here',
headers: { accept: 'application/vnd.github.machine-man-preview+json',
},
});
Those are all the steps required to post a status as a github app. For reference, here are all the bits put together in a gist: