When working with the GitHub API, there are many operations that require a JSON web token (JWT). Just recently I wanted to find out which GitHub accounts (users and organizations) have installed a GitHub App that I published. Looking at the GitHub REST API documentation, I can see the path for listing installations for a GitHub App.
curl
works fine, but the GitHub CLI makes these types of things much easier. I see in the documentation that to list the installations for the app it is this gh api
command:
1
2
3
gh api \
-H "Accept: application/vnd.github.v3+json" \
/app/installations
To make a successful request, though, you will need to generate and pass a JWT. If you didn’t, you would see this helpful message:
1
2
3
4
5
6
7
8
$ gh api \
> -H "Accept: application/vnd.github.v3+json" \
> /app/installations
{
gh: A JSON web token could not be decoded (HTTP 401)
"message": "A JSON web token could not be decoded",
"documentation_url": "https://docs.github.com/rest"
}
To create this JWT, we need some information:
- The GitHub App ID, which will be the issuer claim (“iss”)
- The private key that was originally generated when the App was created
Note: The private key is sensitive data and should be kept in appropriate storage for secrets. In my case, I store this in Azure Key Vault and do not persist it anywhere else.
More information on the authentication workflow for GitHub Apps can be found on my previous blog post: Understanding GitHub App Authentication.
So for me to make this gh api
request to get the App installations, I need to generate a JWT. I couldn’t find a good utility to do this, so I created jwt-creator. This utility takes a private key file as well as standard claims and generates a JWT that can be passed to GitHub.
1
2
3
4
5
6
7
8
9
$ JWT=$(az keyvault secret show \
--vault-name <key_vault_name> \
--name <secret_name> \
--query value -o tsv |
jwt-creator \
--private-key-file - \
--issuer <app_id> \
--issued-at-now \
--expires-in-seconds 300)
This command pulls the private key from my Azure Key Vault and then passes that into jwt-creator
and uses the App ID as the issuer. The result JWT is stored in a variable that can now be passed to gh api
:
1
2
3
4
$ gh api \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer $JWT" \
/app/installations
This request succeeds and now I’m able to see all of the accounts that have installed this GitHub App!