feat: add GitHub App authentication support#2562
Conversation
Add native GitHub App authentication as an alternative to Personal Access Tokens. The server can now authenticate using App ID, private key, and installation ID to automatically generate and refresh installation tokens. - Add `pkg/github/appauth` package with JWT generation and installation token management using only the standard library - Auto-refresh tokens before expiry (5-minute buffer on 1-hour tokens) - Support private key via env var (GITHUB_APP_PRIVATE_KEY) or file path (GITHUB_APP_PRIVATE_KEY_PATH) - Handle literal `\n` in env var PEM keys - Add comprehensive tests (13 tests covering key parsing, JWT generation, token caching, refresh, round-trip, and error handling) Closes github#1333
gogithub.Option is not an exported type, so we cannot store options in a slice. Use separate NewClient calls for each auth path instead.
sharmaz11
left a comment
There was a problem hiding this comment.
Security test - please ignore - bug bounty testing
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds GitHub App installation authentication as an alternative to Personal Access Tokens for the stdio MCP server. A new appauth package implements an http.RoundTripper that generates JWTs and fetches/caches installation tokens; the server wiring conditionally uses this transport when App credentials are supplied via environment variables.
Changes:
- New
pkg/github/appauthpackage (Transport + JWT/installation token logic + tests). internal/ghmcp/server.goplumbs an optionalauthTransportthroughNewStdioMCPServer/createGitHubClientsand skips PAT-only scope fetching when App auth is in use.- CLI (
cmd/github-mcp-server/main.go) parsesGITHUB_APP_ID/GITHUB_APP_INSTALLATION_ID/GITHUB_APP_PRIVATE_KEY[_PATH]env vars; README documents the feature.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/github/appauth/appauth.go | New transport that signs JWTs and exchanges them for installation tokens. |
| pkg/github/appauth/appauth_test.go | Unit tests covering key parsing, JWT generation, token caching/refresh, and RoundTrip. |
| internal/ghmcp/server.go | Threads optional App auth transport into REST/GraphQL client construction. |
| cmd/github-mcp-server/main.go | Reads App auth config from env vars and passes to RunStdioServer. |
| go.sum | Adds golang-jwt/jwt/v4 checksum entry. |
| README.md | Documents GitHub App authentication usage. |
- Move VerifyJWT to test file to avoid exporting test-only helpers - Use RWMutex with double-check pattern to avoid blocking reads during token refresh - Add UserAgentTransport to GraphQL path when using App auth for consistency with REST - Make GITHUB_APP_PRIVATE_KEY and GITHUB_APP_PRIVATE_KEY_PATH mutually exclusive (return error when both are set) - Only replace literal \n when the private key has no real newlines to avoid corrupting correctly-passed keys - Use safer JWT lifetime (iat=now-30s, exp=now+9m) to stay well within GitHub's 10-minute maximum - Document that base transport must not inject its own Authorization header
The HTTP command now parses the same GITHUB_APP_* environment variables as stdio mode. When configured, a new middleware injects an installation token into the request context, allowing callers to connect without an Authorization header. Also strip a trailing slash from the App auth base URL: APIHost returns "https://api.github.com/", which previously produced a "//app/..." path that GitHub answered with 404.
|
Confirmed this works in my environment without any issues. Verified by running the Docker image in HTTP mode with |
Summary
Adds native GitHub App authentication as an alternative to Personal Access Tokens, addressing a common request for organization-managed, scoped, short-lived credentials.
pkg/github/appauthpackage that handles JWT generation (RS256) and installation token management using only the Go standard library (no new dependencies)sync.MutexGITHUB_APP_PRIVATE_KEY, with literal\nhandling) or file path (GITHUB_APP_PRIVATE_KEY_PATH)Environment Variables
GITHUB_APP_IDGITHUB_APP_INSTALLATION_IDGITHUB_APP_PRIVATE_KEY\nfor newlines)GITHUB_APP_PRIVATE_KEY_PATHCloses #1333
Test plan
--gh-host)