← GitHub articles

A bot that uses a broad personal token can do far more than it needs to. A GitHub App limits access to what the bot actually requires.

Build a Scoped Bot: GitHub Apps and Octokit, the Right Way

How to authenticate a GitHub App as an installation, limit it to specific repositories and permissions, and use short-lived tokens.

Shubham Anandani··3 min read

Problem

Personal access tokens are broad. They grant access across every repository the account can reach. GitHub Apps define explicit permissions, install on chosen repositories, and issue installation tokens that expire. Octokit libraries handle token generation and refresh, and actions/create-github-app-token creates scoped tokens inside CI.

Options and trade-offs

  • GitHub App versus OAuth app: Apps offer per-permission and per-repository control. OAuth relies on user-granted scopes.
  • Octokit versus manual token handling: Octokit handles installation tokens and refresh. Manual handling gives more control and more code to maintain.
  • CI token action: mints a scoped token for each job, so you avoid a broad long-lived credential.

My take

Give the app the smallest permission that does the job, install it only on the repositories it needs, and avoid write access to contents unless the bot must change code.

Decision lens: security risk and maintenance cost.

Example setup: a triage bot installed on two repositories with issue write access only. Each CI run mints a token limited to those repositories, adds a comment or label, and never gets contents write access.

Template

src/appClient.js:

import { Octokit } from "@octokit/core";
import { createAppAuth } from "@octokit/auth-app";

export function installationOctokit(appId, privateKey, installationId) {
  return new Octokit({
    authStrategy: createAppAuth,
    auth: { appId, privateKey, installationId }
  });
}

CI: use actions/create-github-app-token with the narrowest permissions and repository list the job needs. Check its README for the current input names.

Checklist

  1. Register a GitHub App and generate a private key.
  2. Set the minimum permissions the bot needs.
  3. Install the app on the target repositories only.
  4. Use Octokit's app authentication for installation tokens.
  5. Mint scoped tokens in CI for each job.
  6. Avoid long-lived personal tokens.

Common mistakes

  • Granting contents write when only issue write is needed.
  • Using a personal access token, which gives the bot a wider blast radius.
  • Assuming tokens never expire. Installation tokens are short-lived, so refresh them.

Where this doesn't apply

  • Organizations that do not allow GitHub Apps or require a different authentication flow.

How to get involved

  • Good first issue: reduce the permissions of an existing bot and add repository scoping.
  • Discussion: what else should a triage bot be allowed to do?

Sources (accessed 2026-10-18)

  • Generating an installation access token for a GitHub App: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app. Installation tokens, permissions, and expiry.
  • Authenticating as a GitHub App installation: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation.
  • Octokit.js: https://github.com/octokit/octokit.js/. App authentication and installation behavior.
  • create-github-app-token action: https://github.com/actions/create-github-app-token.