Skip to content

Commit dccb4b4

Browse files
build(oauth): bake in default OAuth credentials via build-time ldflags
Inject the public OAuth client credentials (stored as the OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET repo secrets) at build time via -ldflags so official binaries and images ship a working default app for zero-config login. Security relies on PKCE, not on the secret. Local/dev builds leave the values empty and continue to require an explicit token or --oauth-client-id. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2b4d5e6 commit dccb4b4

6 files changed

Lines changed: 43 additions & 3 deletions

File tree

.github/workflows/docker-publish.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ jobs:
117117
platforms: linux/amd64,linux/arm64
118118
build-args: |
119119
VERSION=${{ github.ref_name }}
120+
secrets: |
121+
oauth_client_id=${{ secrets.OAUTH_CLIENT_ID }}
122+
oauth_client_secret=${{ secrets.OAUTH_CLIENT_SECRET }}
120123
121124
# Sign the resulting Docker image digest except on PRs.
122125
# This will only write to the public Rekor transparency log when the Docker

.github/workflows/goreleaser.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ jobs:
3838
workdir: .
3939
env:
4040
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41+
OAUTH_CLIENT_ID: ${{ secrets.OAUTH_CLIENT_ID }}
42+
OAUTH_CLIENT_SECRET: ${{ secrets.OAUTH_CLIENT_SECRET }}
4143

4244
- name: Generate signed build provenance attestations for workflow artifacts
4345
uses: actions/attest-build-provenance@v4

.goreleaser.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ builds:
99
- env:
1010
- CGO_ENABLED=0
1111
ldflags:
12-
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
12+
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X github.com/github/github-mcp-server/internal/buildinfo.OAuthClientID={{ .Env.OAUTH_CLIENT_ID }} -X github.com/github/github-mcp-server/internal/buildinfo.OAuthClientSecret={{ .Env.OAUTH_CLIENT_SECRET }}
1313
goos:
1414
- linux
1515
- windows

Dockerfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ COPY . .
2424
COPY --from=ui-build /app/pkg/github/ui_dist/* ./pkg/github/ui_dist/
2525

2626
# Build the server
27+
# OAuth credentials are injected via build secrets so they are not baked into image history; the values are public in practice but kept out of layers.
2728
RUN --mount=type=cache,target=/go/pkg/mod \
2829
--mount=type=cache,target=/root/.cache/go-build \
29-
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
30+
--mount=type=secret,id=oauth_client_id \
31+
--mount=type=secret,id=oauth_client_secret \
32+
export OAUTH_CLIENT_ID="$(cat /run/secrets/oauth_client_id 2>/dev/null || echo '')" && \
33+
export OAUTH_CLIENT_SECRET="$(cat /run/secrets/oauth_client_secret 2>/dev/null || echo '')" && \
34+
CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ) -X github.com/github/github-mcp-server/internal/buildinfo.OAuthClientID=${OAUTH_CLIENT_ID} -X github.com/github/github-mcp-server/internal/buildinfo.OAuthClientSecret=${OAUTH_CLIENT_SECRET}" \
3035
-o /bin/github-mcp-server ./cmd/github-mcp-server
3136

3237
# Make a stage to run the app

cmd/github-mcp-server/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"time"
99

10+
"github.com/github/github-mcp-server/internal/buildinfo"
1011
"github.com/github/github-mcp-server/internal/ghmcp"
1112
"github.com/github/github-mcp-server/internal/oauth"
1213
"github.com/github/github-mcp-server/pkg/github"
@@ -37,6 +38,16 @@ var (
3738
RunE: func(_ *cobra.Command, _ []string) error {
3839
token := viper.GetString("personal_access_token")
3940
oauthClientID := viper.GetString("oauth-client-id")
41+
oauthClientSecret := viper.GetString("oauth-client-secret")
42+
// Fall back to the build-time baked-in client (official releases) when none is
43+
// configured explicitly. The baked-in app is registered on github.com, so it is
44+
// only applied to the default host; GHES/ghe.com users must bring their own
45+
// --oauth-client-id. The secret tracks the id, so an explicitly provided id with
46+
// no secret never picks up the baked-in secret.
47+
if oauthClientID == "" && viper.GetString("host") == "" {
48+
oauthClientID = buildinfo.OAuthClientID
49+
oauthClientSecret = buildinfo.OAuthClientSecret
50+
}
4051
if token == "" && oauthClientID == "" {
4152
return errors.New("authentication required: set GITHUB_PERSONAL_ACCESS_TOKEN, or pass --oauth-client-id to log in via OAuth")
4253
}
@@ -112,7 +123,7 @@ var (
112123
}
113124
oauthConfig := oauth.NewGitHubConfig(
114125
oauthClientID,
115-
viper.GetString("oauth-client-secret"),
126+
oauthClientSecret,
116127
scopes,
117128
viper.GetString("host"),
118129
viper.GetInt("oauth-callback-port"),

internal/buildinfo/buildinfo.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Package buildinfo contains variables that are set at build time via ldflags.
2+
// These allow official releases to ship default OAuth credentials so users can
3+
// log in without configuring their own OAuth app. The values are public in
4+
// practice (security relies on PKCE, not on the client secret), but are kept out
5+
// of source and injected at build time.
6+
//
7+
// Example:
8+
//
9+
// go build -ldflags="-X github.com/github/github-mcp-server/internal/buildinfo.OAuthClientID=xxx"
10+
package buildinfo
11+
12+
// OAuthClientID is the default OAuth client ID, set at build time. Empty in
13+
// local/dev builds.
14+
var OAuthClientID string
15+
16+
// OAuthClientSecret is the default OAuth client secret, set at build time. For
17+
// public OAuth clients it is not truly secret per OAuth 2.1 — PKCE provides the
18+
// security — but it is still injected at build time rather than committed.
19+
var OAuthClientSecret string

0 commit comments

Comments
 (0)