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.
| Comparison | GitHub App | OAuth App |
|---|---|---|
| Act as | Bot (system) | User |
| Permission scope | Fine-grained permissions | OAuth scopes (coarse) |
| Access range | Per-repository installation | Full scope authorized by user |
| Token type | Installation Access Token | User-to-Server Token |
| Rate limit | 5,000 req/h per installation | 5,000 req/h per user |
| Webhook support | Configurable per app | Limited |
| Marketplace | Supported | Supported |
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");
});
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_TOKENdo 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.
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 Parameter | Description |
|---|---|
app-id | GitHub App ID (recommended to store as vars.APP_ID) |
private-key | RSA private key (must be stored as a secret) |
owner | Target Organization/user for the token (defaults to the current repo owner) |
repositories | Comma-separated repository names to access |
Steps to Create a GitHub App
- Register the App: Go to GitHub Settings > Developer settings > GitHub Apps > New GitHub App
- Basic settings: Enter App name, Homepage URL, and Webhook URL
- Permission settings: Select only the minimum required permissions (principle of least privilege)
- Generate private key: After creating the App, click "Generate a private key" to generate and download the RSA key pair
- Install: Install the App on your Organization/repository
- Store secrets: Register the App ID and private key as CI/CD secrets
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.
| Category | Permission Examples |
|---|---|
| Repository | Contents, Pull requests, Issues, Actions, Deployments |
| Organization | Members, Projects |
| Account | Profile, 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.