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.