Skip to main content

GitHub App Basics

A GitHub App is an official application integration mechanism for building automation tools, bots, and service integrations with GitHub's API and Webhooks. It features fine-grained permissions and a dedicated authentication flow, making it more secure than OAuth Apps.

GitHub App vs OAuth App​

There are two main ways to integrate with GitHub.

ComparisonGitHub AppOAuth App
Act asBot (system)User
Permission scopeFine-grained permissionsOAuth scopes (coarse)
Access rangePer-repository installationFull scope authorized by user
Token typeInstallation Access TokenUser-to-Server Token
Rate limit5,000 req/h per installation5,000 req/h per user
Webhook supportConfigurable per appLimited
MarketplaceSupportedSupported

GitHub recommends GitHub Apps, and using a GitHub App is best practice for any new integration development.

Key Concepts​

App Owner​

A GitHub App is owned by an Organization or personal account. The owner manages the App settings (permissions, Webhook URL, Private Key, etc.).

Installation​

When a user or Organization "installs" a GitHub App, it grants access to specific repositories (or all repositories). Multiple installations can exist for a single App.

Private Key​

GitHub Apps use an RSA private key to generate JWTs (JSON Web Tokens). This private key should be held only by the App Owner and managed securely.

Installation Access Token​

A short-lived (1 hour) token used by the GitHub App to call APIs. It is issued by making a request to the GitHub API using a JWT.

Authentication Flow​

GitHub App authentication is a two-step process.

Step 1: Generate JWT​

Generate a JWT using the GitHub App ID and RSA private key. The JWT's maximum expiration is 10 minutes.

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

const auth = createAppAuth({
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
});

// Initialize Octokit with JWT (App authentication)
const appOctokit = new Octokit({
authStrategy: createAppAuth,
auth: {
appId: process.env.GITHUB_APP_ID,
privateKey: process.env.GITHUB_APP_PRIVATE_KEY,
},
});

Step 2: Obtain an Installation Access Token​

Use the JWT to obtain an Access Token for a specific installation.

// Get Access Token for a specific installation ID
const installationOctokit = await appOctokit.auth({
type: "installation",
installationId: Number(process.env.GITHUB_APP_INSTALLATION_ID),
});

// API call using Installation Access Token
const octokit = new Octokit({ auth: installationOctokit.token });
const { data } = await octokit.rest.repos.listForInstallation();

Webhook Event Processing​

A GitHub App can receive events (push, pull_request, issues, etc.) occurring in installed repositories via Webhooks. The received webhook payload must be verified using the signature in the X-Hub-Signature-256 header.

import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature =
"sha256=" +
createHmac("sha256", secret).update(payload).digest("hex");

// Use timingSafeEqual to prevent timing attacks
return timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}

// Webhook endpoint example with Express.js
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-hub-signature-256"] as string;
const payload = req.body.toString("utf8");

if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Unauthorized");
}

const event = req.headers["x-github-event"];
const body = JSON.parse(payload);

if (event === "pull_request" && body.action === "opened") {
// Handle pull request opened event
console.log(`PR opened: ${body.pull_request.title}`);
}

res.status(200).send("OK");
});
Security Note

Webhook signature verification is mandatory. Simple string comparison with === is vulnerable to timing attacks — always use crypto.timingSafeEqual.

Using GitHub Apps with GitHub Actions​

Limitations of the Default GITHUB_TOKEN​

The default token in GitHub Actions (secrets.GITHUB_TOKEN) has several restrictions.

  • No cross-repository access: Cannot make API calls to other repositories
  • Cannot trigger new workflows: Events triggered by GITHUB_TOKEN do not start new workflows (to prevent infinite loops)
  • Fixed permissions: Limited fine-grained permission control per workflow

Using a GitHub App Token​

Using a GitHub App in Actions allows you to work around these limitations.

.github/workflows/cross-repo-example.yml
name: Cross-repository operation

on:
push:
branches: [main]

jobs:
update-other-repo:
runs-on: ubuntu-latest
steps:
- name: Generate GitHub App token
id: generate-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
# Access to another repository can also be specified
repositories: "other-repo"

- name: Use the token for API call
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
gh api repos/my-org/other-repo/dispatches \
--method POST \
--field event_type=deploy-triggered

The actions/create-github-app-token Action​

actions/create-github-app-token is an official GitHub Action for easily generating a GitHub App installation token within a workflow.

Input ParameterDescription
app-idGitHub App ID (recommended to store as vars.APP_ID)
private-keyRSA private key (must be stored as a secret)
ownerTarget Organization/user for the token (defaults to the current repo owner)
repositoriesComma-separated repository names to access

Steps to Create a GitHub App​

  1. Register the App: Go to GitHub Settings > Developer settings > GitHub Apps > New GitHub App
  2. Basic settings: Enter App name, Homepage URL, and Webhook URL
  3. Permission settings: Select only the minimum required permissions (principle of least privilege)
  4. Generate private key: After creating the App, click "Generate a private key" to generate and download the RSA key pair
  5. Install: Install the App on your Organization/repository
  6. Store secrets: Register the App ID and private key as CI/CD secrets
tip

Store the private key (.pem file) securely. In GitHub Actions, register it as a repository secret or Organization secret and access it via environment variables. When saving a multi-line private key as a secret, paste it with newlines intact for it to be handled correctly.

Permission (Permissions) Types​

The main permission categories available for GitHub Apps are as follows.

CategoryPermission Examples
RepositoryContents, Pull requests, Issues, Actions, Deployments
OrganizationMembers, Projects
AccountProfile, Email addresses

Each permission has three levels: read, write, and none. Granting only the minimum necessary permissions is a security best practice.

Summary​

GitHub Apps are a secure and flexible mechanism well-suited for service and bot integrations. By combining fine-grained permission access, JWT-based authentication, and event-driven processing via Webhooks, you can build robust automation. They are also effective for cross-repository operations and workflow trigger control in GitHub Actions.