Authenticating as a GitHub App in a GitHub Actions workflow
Tags: github, actions, ci
Published: 2020-05-24 09:30:02

---
title: Authenticating as a GitHub App in a GitHub Actions workflow
published: true
description: Oftentimes we need to make our GitHub Actions workflow communicate with GitHub APIs. Many authentication methods exist, and each comes with its own pros and cons.
tags: github, actions, ci
---

Oftentimes we need to make our GitHub Actions workflow communicate with GitHub APIs. Many authentication methods exist, and each comes with its own pros and cons. You can use the built-in `GITHUB_TOKEN` secret, a personal access token, or a GitHub App.

The first two methods are pretty well-documented, but soon I faced some limitations when using these two methods.

- The `GITHUB_TOKEN` cannot access other private repositories.
- Using my personal access token means giving access to every other private repositories that I have access to (including those in other organizations).
- Creating a bot account costs a team seat.

There exists a third method — I can create a GitHub App and grant it access to select repositories, and make the workflow authenticate with the GitHub API as that app. However, I did not see much documentation about it, and I found the setup process to be more complicated. That’s why I set out to write this article.

I will also introduce a tool I created, [obtain-github-app-installation-access-token](https://github.com/dtinth/obtain-github-app-installation-access-token), to make this process more convenient.

## An overview of available authentication methods

### Method 1: Using [the built-in `GITHUB_TOKEN` secret](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token)

- ✅ **No set-up required.** It just works, even for forked repositories.
- ✅ **The token [can access only the repo containing the workflow file](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#about-the-github_token-secret).** The token cannot be used to access other private repositories.
- 😕 **The token can access only the repo containing the workflow file.** If you need to access other private repos (or need a write access to other public repos), this token won’t work.
- 😕 **The token [cannot trigger other workflows](https://help.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token).** If you have a workflow that [creates a release](https://github.com/actions/create-release) and another workflow that gets run [when someone creates a release](https://help.github.com/en/actions/reference/events-that-trigger-workflows#release-event-release), then the first workflow won’t trigger the second workflow if it uses this token.

### Method 2: Using your personal access token

- ✅ **Simple to set up.** You can get a token at [https://github.com/settings/tokens](https://github.com/settings/tokens)
- **✅ The token can trigger other workflows.**
- **✅ It can access all your repos you have access to.** This is convenient because you can access other repositories without having to do any other extra set up.
- 😕 **It can access all your repos you have access to.** You don’t have a fine grained control over what repositories this token can access, because this token represents _you_. Personally, I do not put my personal access token in any place where access can be shared with others. That means I do not use it with any CI system, because the token can be leaked when I share a repository with another collaborator.
- 😕 **It is bound to a person.** The owner of the token leaving the organization can cause your workflow to break.

### Method 3: Creating a bot account and using _its_ personal access token

- **✅ Simple to set up.**
- **✅ The token can trigger other workflows.**
- **✅ You can control which repositories your token has access to** by granting your bot access to select repositories.
- 😕 **It costs a team seat.** That’s an extra \$4/month for each bot account.
- 😕 **Managing this account requires sharing passwords.**

### Method 4: **Creating a GitHub App and generating tokens from it**

- **✅ You can control which repositories your token has access to** by installing the GitHub App to select repositories.
- **✅ An organization can own many GitHub Apps, and they don’t cost a team seat.**
- **✅ GitHub Apps has a more fine-grained permission model.**
- **✅ The token can trigger other workflows.**
- 😕 **Not officially documented.** The GitHub Actions’ official docs only mentions [using a personal access token](https://help.github.com/en/actions/reference/events-that-trigger-workflows#triggering-new-workflows-using-a-personal-access-token) but not GitHub Apps. It works nonetheless.
- 😕 **The set-up is a bit more complicated.** You need to [register a new GitHub App](https://developer.github.com/apps/building-github-apps/creating-a-github-app/), [install it](https://developer.github.com/apps/installing-github-apps/), [generate a private key](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#generating-a-private-key), and write some extra code in your CI pipeline to [mint a JWT from that private key](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app) and finally [exchange that JWT for an ephemeral installation access token](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation) that your workflow can use with normal GitHub APIs.

## Let’s make a GitHub App!

First, go to [https://github.com/settings/apps](https://github.com/settings/apps) (for personal apps) or [https://github.com/organizations/&lt;organizationName&gt;/settings/apps](https://github.com/organizations/$organizationName/settings/apps) (for organization-owned app) and click the **New GitHub App** button.

- For the **Homepage URL** (a mandatory field), maybe you can put a link to your repository or organization.
- Uncheck the **Active** checkbox under the **Webhook** section. Otherwise, you need to put in a webhook URL.
- Select the desired repository permissions and click the **Create GitHub App** button.
- After creating, note the **App ID** at the top of the page.

## Generate a private key

Scroll all the way down to the bottom and click the **Generate a private key** button. You will get a PEM file.

## Install the app

Scroll back up to the top and click the **Install App** link in the sidebar and select the organization/account and repositories to grant access to. After installation, note the **Installation ID**, which you can find in the URL (it should end with `installations/<installationId>`).

## Checkpoint!

By now we should have these information:

- **✅** The App ID.
- **✅** The Installation ID.
- ✅ The private key.

## Let’s get an installation access token

You can write your own script that [mints a JWT](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app) and [exchanges it for an installation access token](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation). You can also use my Node.js script.

{% github dtinth/obtain-github-app-installation-access-token %}

For convenience, I made a web page where you can fill in forms and get a GitHub Actions workflow script. You can access it here:

→ [https://dtinth.github.io/obtain-github-app-installation-access-token/](https://dtinth.github.io/obtain-github-app-installation-access-token/)

![https://dev-to-uploads.s3.amazonaws.com/i/vgzb86xz26057o9mdvwu.png](https://dev-to-uploads.s3.amazonaws.com/i/vgzb86xz26057o9mdvwu.png)

This gives you **a credentials token**. This is not an installation access token that you can use to make API calls. To get a usable token, we need mint a JWT and exchange it for an installation access token. The `obtain-github-app-installation-access-token` makes this simpler by handling that process for you and now all you need is a credentials token.

![](https://dev-to-uploads.s3.amazonaws.com/i/v5rbmk1kgayw0ewit1m3.png)

You can invoke it manually like this:

```bash
$ npx obtain-github-app-installation-access-token ci "eyJhcHBJZCI6IjY2..."
v1.16d59ba2d3be0712c96451773477395767586351
```

Now you have a usable token:

```bash
$ curl https://api.github.com/installation/repositories \
    -H 'Authorization: Bearer v1.16d59ba2d3be0712c96451773477395767586351' \
    -H 'Accept: application/vnd.github.machine-man-preview+json'
{
  "total_count": 1,
  "repository_selection": "selected",
  ...
}
```

Note that this token expires automatically after 1 hour. That’s why we need to put the script that obtains the installation access token into our CI workflow script.

## Set it up on CI

If you use the above web page to generate a credentials token, it will also give you the GitHub Actions workflow script that you can use. Setting it up requires 2 steps:

1. Adding a secret, `GH_APP_CREDENTIALS_TOKEN`, to the GitHub repository.
2. Adding a workflow script, shown below

   ```yaml
   - name: Obtain GitHub App Installation Access Token
     id: githubAppAuth
     run: |
       TOKEN="$(npx obtain-github-app-installation-access-token ci ${{ secrets.GH_APP_CREDENTIALS_TOKEN }})"
       echo "::add-mask::$TOKEN"
       echo "::set-output name=token::$TOKEN"
   - name: Use the obtained token
     run: |
       curl -X POST -H 'Content-Type: application/json' \
         -d '{"context":"test","state":"success"}' \
         "https://api.github.com/repos/$GITHUB_REPOSITORY/statuses/$GITHUB_SHA?access_token=$GITHUB_TOKEN"
     env:
       GITHUB_TOKEN: ${{ steps.githubAppAuth.outputs.token }}
   ```

## Conclusions

I hope this article shows how using GitHub Apps can add more flexibility to your CI pipeline without compromising security or costing extra money.

Hope you find this useful, and thanks for reading!