Support GitHub App authentication across multiple installations - #3199
Open
vedant381 wants to merge 1 commit into
Open
Support GitHub App authentication across multiple installations#3199vedant381 wants to merge 1 commit into
vedant381 wants to merge 1 commit into
Conversation
A GitHub App can be installed on several accounts, but each installation has its own ID and mints its own access token. Today the server takes a single installation ID, so an enterprise whose repositories are spread across organizations needs one server process per organization. Make --app-installation-id optional. Without it, the server lists the app's installations, caches the account-to-installation map, and mints a token per installation on demand, routing each API request to the installation that owns the resource it addresses: REST requests by the owner in the path, GraphQL requests by the owner or login variable. Routing needs the request, which the existing func() string token provider cannot see, so BearerAuthTransport gains an optional RequestTokenProvider that takes precedence over it. A request that names no owner, or names an account the app is not installed on, is sent unauthenticated rather than falling back to another installation's token, so a misrouted call fails visibly instead of running against the wrong organization. Passing --app-installation-id keeps the existing single-installation behavior unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes
--app-installation-idoptional so a single app ID and private key can authenticate against every organization a GitHub App is installed on, routing each API request to the installation that owns the resource it addresses.Why
A GitHub App can be installed on several accounts, but each installation has its own ID and mints its own access token. The current implementation takes one installation ID, so an enterprise with repositories spread across organizations needs one server process per organization.
Fixes #2885
What changed
internal/githubapp: newMultiProvider, which lists the app's installations (GET /app/installations), caches the account-login-to-installation map, and lazily creates a cached single-installationProviderper installation. The directory is refreshed on a lookup miss, at most every 10 minutes, so installing the app on a new organization is picked up without a restart.internal/githubapp: newOwnerFromRequest, which determines the owning account from an outbound request — REST from the path (/repos/{owner}/…,/orgs/{org}/…,/users/{user}/…, including the GHES/api/v3prefix), GraphQL from theowner/loginvariable in the request body (read viaGetBody, so the body is left intact for the transport below).pkg/http/transport:BearerAuthTransportgains an optionalRequestTokenProvider func(*http.Request) string. Routing needs the request, which the existingfunc() stringprovider cannot see. It takes precedence overTokenProviderandToken; when all are unset, behavior is unchanged.cmd/github-mcp-server,internal/ghmcp,pkg/github: plumb the request-scoped provider through, and select multi-installation mode when--app-installation-idis omitted. Passing an installation ID keeps the existing single-installation path exactly as it was.docs/github-app-auth.md: new "Multiple organizations" section covering configuration, how routing works, and its limits.Usage:
MCP impact
Authentication configuration only; no tool schemas or behavior change.
Prompts tested (tool changes only)
n/a — no tool changes.
Security / limits
A request that names no owner, or names an account the app is not installed on, is sent unauthenticated rather than falling back to another installation's token — a misrouted call fails visibly (401/404) instead of silently running against the wrong organization. The one deliberate trade-off is that endpoints which are not owner-scoped (
/user,/rate_limit,/repositories/{id}) do not work in multi-installation mode;--app-installation-idremains available to authenticate as one specific installation. This is called out in the docs.Installation tokens are still scoped by
AllowedHosts, unchanged. The private key handling is untouched.Tool renaming
Lint & tests
go test ./...— full suite passes../script/lint— the pinnedgolangci-lintv2.9.0 cannot read Go 1.27 export data on my machine (export data version 4 is greater than maximum supported version 2). I ran v2.13.2 instead: it reports no findings in any file this PR touches (the 3 remaining findings are pre-existing, inpkg/http/transport/etag_test.goand elsewhere, and surface only because that version is newer than the pinned one).gofmt -sis clean.New tests cover owner extraction from REST paths and GraphQL bodies (including the body-not-consumed contract), per-owner token routing, case-insensitive account lookup, directory caching and refresh-on-miss, listing failures, and the transport's provider precedence.
Docs