Generate JWTs for GitHub App authentication. Direct JWT generation for app-level operations, installation discovery, and bootstrapping workflows.
Generate JWT tokens for GitHub App authentication to access app-level APIs like installation discovery and metadata retrieval. Used when the GitHub CLI is unavailable or for custom implementations requiring manual token generation.
/plugin marketplace add adaptive-enforcement-lab/claude-skills/plugin install patterns@ael-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples.mdreference.mdscripts/example-1.mermaidscripts/example-2.yamlscripts/example-3.yamlscripts/example-4.yamlscripts/example-5.yamlscripts/example-6.yamlscripts/example-7.yamlscripts/example-8.yamlJWTs authenticate your GitHub App itself, not a specific installation. They enable:
JWT Limitations
- Cannot access repository contents
- Cannot create issues, pull requests, or commits
- 10-minute expiration (maximum allowed)
- App-level permissions only
See examples.md for detailed code examples.
The GitHub CLI handles JWT generation automatically when using GitHub App credentials.
jobs:
list-installations:
runs-on: ubuntu-latest
steps:
- name: List app installations
env:
GH_APP_ID: ${{ secrets.CORE_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.CORE_APP_PRIVATE_KEY }}
run: |
# gh CLI generates JWT automatically
gh api /app/installations \
--jq '.[] | {id: .id, account: .account.login}'
How it works:
GH_APP_ID + GH_APP_PRIVATE_KEY triggers automatic JWT generationFor custom implementations or languages without GitHub CLI support.
jobs:
manual-jwt:
runs-on: ubuntu-latest
steps:
- name: Generate JWT manually
id: jwt
env:
APP_ID: ${{ secrets.CORE_APP_ID }}
PRIVATE_KEY: ${{ secrets.CORE_APP_PRIVATE_KEY }}
run: |
# Install JWT tool
npm install -g jsonwebtoken
# Create JWT generation script
cat > generate-jwt.js << 'EOF'
const jwt = require('jsonwebtoken');
const fs = require('fs');
const appId = process.env.APP_ID;
const privateKey = process.env.PRIVATE_KEY;
const now = Math.floor(Date.now() / 1000);
const payload = {
iat: now - 60, // Issued 60 seconds in past
exp: now + (10 * 60), // Expires in 10 minutes
iss: appId
};
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
console.log(token);
EOF
# Generate JWT
JWT_TOKEN=$(node generate-jwt.js)
echo "::add-mask::$JWT_TOKEN"
echo "token=$JWT_TOKEN" >> $GITHUB_OUTPUT
- name: Use JWT
env:
GITHUB_TOKEN: ${{ steps.jwt.outputs.token }}
run: |
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app
Security: Mask JWT Token
Always use
echo "::add-mask::$JWT_TOKEN"to prevent token exposure in logs.
For Python-based workflows and automation.
jobs:
python-jwt:
runs-on: ubuntu-latest
steps:
- name: Generate JWT with Python
id: jwt
env:
APP_ID: ${{ secrets.CORE_APP_ID }}
PRIVATE_KEY: ${{ secrets.CORE_APP_PRIVATE_KEY }}
run: |
pip install PyJWT cryptography
python << 'EOF'
import jwt
import time
import os
app_id = os.environ['APP_ID']
private_key = os.environ['PRIVATE_KEY']
now = int(time.time())
payload = {
'iat': now - 60,
'exp': now + (10 * 60),
'iss': app_id
}
token = jwt.encode(payload, private_key, algorithm='RS256')
# Mask token in logs
print(f"::add-mask::{token}")
# Output token
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"token={token}\n")
EOF
- name: Use JWT
env:
GITHUB_TOKEN: ${{ steps.jwt.outputs.token }}
run: |
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/app/installations
See reference.md for additional techniques and detailed examples.
See examples.md for detailed code examples.
See examples.md for code examples.
See reference.md for complete documentation.