There is a lot of confusion of what GitHub (access) tokens are and how you should use them for automating things inside of GitHub. There are three main types of tokens:
- Personal Access Tokens (PATs)
- The GITHUB_TOKEN environment variable (explainer here)
- An access token created from a GitHub App (explainer here)
You can use these tokens to authenticate to GitHub and perform actions with it, like cloning repositories, making API calls, etc.
Photo by James Sutton on Unsplash
1. Personal Access Tokens (PATs)
This type of token is often the first thing that people start to use when automating things. It is the same for different CI/CD systems like Azure DevOps that I’ve used before. A personal access token is inked to a user account and can be set to automatically expire. These days they are prefixed with ghp
so that the secret scanner can detect them more easily.
You can use the PAT to do anything the user account that created them can do (as long as it is given the appropriate scopes for it):
- Create repositories
- Create issues
- Create pull requests
- Read security alerts
- etc.
The downsides of Personal Access Tokens
This token can actually do anything
the user can do, but for anything
the user has access to! If the user has access to repos, organizations or enterprises that have internal/private repos, the PAT has access to it! The only thing you can do is limit the scope it has, but not organizations or repos. (note: this is planned on the roadmap and very needed).
Because of this, using a PAT poses a big security risk. Do not hand them out to any random action in your workflows: if they store the PAT somewhere, they can read all your private repos (and more!). I recommend to stay away from using PATs if you can help it.
Additionally, they are linked to a user, so when that user leaves the company (and the user account is disabled): all automations using their PAT will stop working!
Note: the only valid reason for using the GitHub Token is for accessing things in the API from the Enterprise level, in case you need to automate things like the creation of users, which should be done with SCIM as a best practice. So still, most of this reason is not valid 😏.
2. The GITHUB_TOKEN environment variable
The GITHUB_TOKEN is an environment variable you can use in your workflows by injecting it wherever you need it: $
. The token is autogenerated and is not stored anywhere. It is a token that is only valid for the duration of the workflow it was created for. By default it has read and write access to the repository the workflow is running for. That means you cannot use this token for another repository. You can use it to do things like create issues, create pull requests, or comment on them. Depending on the context, it might get less permissions, for example: when running on a Pull Request coming from a fork, it will only have read access to the repository.
You can also give it a specific scope by setting that inside the workflow:
<span class="na">permissions</span><span class="pi">:</span>
<span class="na">contents</span><span class="pi">:</span> <span class="s">read</span>
<span class="na">pull-requests</span><span class="pi">:</span> <span class="s">write</span>
This way, the token cannot be used for creating an issue for example, so even if the action you use tries to create an issue, the token doesn’t have the permissions to do so and thus it will fail.
If you want to learn more on the GITHUB_TOKEN, I have explained its use and how to limit what it can do in this short video.
3. An access token created from a GitHub App
You can use a GitHub App to have very specific access to one or more repositories. I use this for example for setting up a (lot of) Jenkins connections: each team at my customer has their own set of Jenkins jobs that should trigger when someone pushes a new commit to their GitHub repository. By giving each team their own GitHub App, I can limit the access the App has to only the repos that are relevant to them (by installing the App on only their repositories and not all repos in the organization). Since this token is tied to the installation of the App, we call this an installation token.
Note: on github.com you can only create 100 apps per organization. On GitHub Enterprise Server this restriction does not apply.
With a GitHub App, you get an AppId and a private key in the PEM format. You can create a GitHub App on a website and then install it on the repositories (or organizations) you want to use it for. You can then use the AppId and the private key to create an access token that can be used to access the repositories.
There are multiple options to get the token, depending where you want to use it:
- Normal shell scripts
- Use an action
- Use a library (can be included as an extension to the GitHub CLI)
Note: this token is only valid for 1 hour, after that you need to refresh it or your calls will fail
For an explainer of how to create a GitHub App and then use it in for example a GitHub workflow, check my video on it here:
Get an access token for an App with shell scripts
I have a GitHub Gist that shows you how to get an access token from a GitHub App from shell scripts (I have an example how to call the shell script from PowerShell as well).
The steps are:
- Get the AppId and the private key from the GitHub App you created
- Generate a signed JWT token with the AppId and the private key
- With the JWT toke, get the installations of the app (you need the installation id to create the token)
- Create a token for the installation
- Use the token to access the repositories
You can then use this token for any API call you want to make (and that is accessible for an App. Creating codespaces is not for example, since you can only create a codespace for your own user account).
In the gist you also find an example of using git config rewriting to overwrite the global git config with a rule that maps a SSH setup to a HTTPS setup that includes the access token. This can be helpful to help custom scripts / tools that can are preconfigured with only SSH support.
Get an access token for an App with an action in a workflow
Inside of a workflow I always use the action from Peter Murray. It takes an application_id and an application_private_key and generates a token that can be used to access the repositories. You can even give the token it creates a scope by sending in the permissions
parameter.
Get an access token for an App with a library
My buddy Bassem Dghaidi has created a bash library that does the work for you, and can be included in the GitHub CLI. You can find it in this repo with all the setup needed. You can for example use this easily in a Docker container when you are automating things.
Note: for installation as an extension in the CLI you need an authenticated CLI session first, so that is not helpful for automation purposes.
The downside of using GitHub Apps
GitHub Apps are great for automating things: they have more access then the GITHUB_TOKEN (across repositories for example) and do not have the issue that they operate from a user account and have access to everything the user has access to (like a PAT).
There are some downsides as well:
- ‘Only 100 apps’ per organization on github.com (so public repos or GitHub Enterprise Cloud: GHEC).
- Only minimal options to automate the setup of the apps themselves: you can use a manifest to create them, but installing them on repositories (even the ones you own), is not possible with an API.
- Little to no support on external tools: most of them want to have the token and cannot generate that token from the AppId and the private key, so you need to do that yourself (and be aware of the 1 hour expiration!).
Some things I have used GitHub Apps for:
- Installing and configuring self hosted runners.
- Configuring access from Jenkins pipelines to GitHub repos.
- Inside workflows everywhere: I like the limited access the App has and do not want to give anything my PAT as it has way to much access!