For automation, use a GitHub App and short-lived installation tokens. Reserve personal access tokens for people. A personal access token (PAT) acts as a person, lasts as long as it was created to, and cannot be rotated by an application. An installation token acts as an app, is limited to what you choose, and expires in an hour.
If your pipelines still authenticate with a PAT from someone's account, this is the cheapest security improvement available to you.
The problem with personal access tokens in automation
- They belong to a person. When that person leaves, changes team or loses access, the pipeline breaks or, worse, keeps working with permissions nobody reviewed.
- They are long-lived. Many are created with no expiry, or a year, and copied into CI variables, wikis and chat messages.
- They are broad. Even fine-grained tokens are often created with more access than the job needs, because narrowing them is fiddly.
- They cannot be rotated by software. GitHub does not provide an API to create personal access tokens, and a GitHub App cannot create them either. We checked this against a live GitHub account with a throwaway token: the create call returned 404. Any "automatic PAT rotation" claim should be treated with suspicion. The honest workflow is a manual step: create a new token on github.com and re-import it.
What a GitHub App gives you
A GitHub App is an identity of its own, installed on the repositories or organisations you choose. It authenticates in two steps: it proves itself with a signed assertion, then asks GitHub for an installation access token.
| Personal access token | App installation token | |
|---|---|---|
| Acts as | A person | The app |
| Lifetime | As configured, often months | One hour |
| Scope | Whatever the person granted | The repositories and permissions you select, and narrower per request |
| Survives staff changes | No | Yes |
| Created by software | No | Yes, on demand |
| Revoke | Delete the token | Remove the installation or revoke the token |
The practical effect: a leaked installation token is useful for at most an hour and only against what the app was allowed to touch.
Getting it right
- Create one app per purpose. A deployment app, a release app, a read-only audit app. Do not build one app that can do everything.
- Grant the smallest permissions. For a job that checks out code and reads metadata,
contents: readandmetadata: readare enough. Treat permissions that can change workflows, secrets, administration or hooks as high risk, and require a conscious decision to allow them. - Limit repositories. Install on the repositories the pipeline needs, not on "all repositories".
- Mint tokens on demand and refresh before expiry. Do not cache a token past its expiry, and refresh a little early. After a 401, 403 or 404, never keep serving a held token.
- Narrow per consumer. Where one app serves several pipelines, issue each a token scoped to only its repositories and permissions, never wider than the app's own.
- Protect the app's private key. It is the one long-lived secret in this model. Keep it in a vault, rotate it on a schedule, and never in a repository.
- Revoke early. When a job ends or a grant is withdrawn, revoke the token instead of waiting for it to expire.
- Rate-limit minting. A runaway pipeline should not be able to request thousands of tokens.
- Audit. Record who was issued a token, for which scope, and when. Never record the token itself.
Migrating from PATs
- Inventory the PATs in use: which pipelines, which accounts, which repositories.
- Create an app per pipeline family, with the minimum permissions.
- Replace the PAT with a step that obtains an installation token at the start of the job.
- Delete the PAT. Do this the same day you cut over, or the old token stays as a back door.
- Keep a short list of cases that genuinely need a PAT (some third-party integrations do) and put an owner and an expiry date on each.
How ZenXPAM supports this
ZenXPAM brokers GitHub App installation tokens as a non-human identity. No credential is stored: a short-lived token is issued on demand, limited to the repositories and permissions configured for the identity, and narrowed further per consumer, never widened. High-risk permissions are rejected unless a platform administrator allows them, and tokens are revoked at GitHub when a grant is withdrawn or the identity is disabled. The TypeScript and .NET SDKs and the zenx-secret CLI honour the token's expiry and never use one after it has expired. This was verified against a real GitHub App: the token was limited to the chosen repository and permissions, and removing the app's grant made GitHub reject the token at once. For PATs that must remain, ZenXPAM inventories them and treats rotation as the manual step it is. See non-human identity governance or book a demo.
FAQ
Can a GitHub App create a personal access token? No. GitHub offers no API for creating PATs, for apps or anyone else.
How long does an installation token last? One hour.
Is a fine-grained PAT good enough? It is better than a classic PAT, but it still belongs to a person and still has to be rotated by hand. An app is the better fit for automation.
Do GitHub Actions need any of this? Workflows already receive a short-lived GITHUB_TOKEN. Use an app when a pipeline needs access beyond the repository the workflow runs in, or when something outside Actions needs to call GitHub.
What about GitLab or Azure DevOps? The principle (short-lived, narrowly scoped, non-personal credentials) applies, but the mechanisms differ. ZenXPAM's GitHub App support is built today; other CI providers are on the roadmap.

