From 484a720f06f2c732e335590a9d9a9685e5e8c931 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:46:44 +0000 Subject: [PATCH 1/4] Initial plan From ab6f8ee41450bcb454b14ecac34f5f99b5dad56a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:52:51 +0000 Subject: [PATCH 2/4] Improve OIDC env var validation: fail-fast during config validation and record startup errors - Add ACTIONS_ID_TOKEN_REQUEST_URL env var check in validateAuthConfig() so github-oidc misconfiguration is caught at config-load time (fail-fast) - Record OIDC server errors in serverErrors during launcher.New() so /health reports them as "error" immediately at startup - Add test for missing OIDC env var in validation_test.go - Add test for early error recording in launcher_test.go - Set OIDC env var in existing tests that validate valid github-oidc configs Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/b2ecfab2-61b7-47ed-b0ab-6a60c21cffd0 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- internal/config/config_stdin_test.go | 3 +++ internal/config/validation.go | 11 +++++++++++ internal/config/validation_test.go | 21 +++++++++++++++++++++ internal/launcher/launcher.go | 11 ++++++++--- internal/launcher/launcher_test.go | 26 ++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/internal/config/config_stdin_test.go b/internal/config/config_stdin_test.go index 957a01f34..9f1c28b05 100644 --- a/internal/config/config_stdin_test.go +++ b/internal/config/config_stdin_test.go @@ -1027,6 +1027,9 @@ func TestConvertStdinConfig_TrustedBots(t *testing.T) { // TestConvertStdinServerConfig_HTTPWithAuth tests that auth config is properly converted. func TestConvertStdinServerConfig_HTTPWithAuth(t *testing.T) { + // OIDC validation now checks that ACTIONS_ID_TOKEN_REQUEST_URL is set + t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", "https://token.actions.example.com") + t.Run("auth with explicit audience", func(t *testing.T) { server := &StdinServerConfig{ Type: "http", diff --git a/internal/config/validation.go b/internal/config/validation.go index a29bc2fbb..acf6291c3 100644 --- a/internal/config/validation.go +++ b/internal/config/validation.go @@ -258,6 +258,17 @@ func validateAuthConfig(auth *AuthConfig, serverName, jsonPath string) error { return rules.UnsupportedType("type", auth.Type, authPath, fmt.Sprintf("Unsupported auth type %q. Currently only \"github-oidc\" is supported", auth.Type)) } + // Fail-fast: check that required OIDC environment variables are present. + // This catches misconfigurations at config-load time rather than deferring + // the error to the first request against this server. + if os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL") == "" { + logValidateServerFailed(serverName, "ACTIONS_ID_TOKEN_REQUEST_URL is not set") + return rules.MissingRequired( + "ACTIONS_ID_TOKEN_REQUEST_URL", "environment", authPath, + "Server requires OIDC authentication but ACTIONS_ID_TOKEN_REQUEST_URL is not set. "+ + "OIDC auth requires running in GitHub Actions with `permissions: { id-token: write }`") + } + logValidation.Printf("Auth config validated: server=%s, type=%s", serverName, auth.Type) return nil } diff --git a/internal/config/validation_test.go b/internal/config/validation_test.go index 0b16704d4..c5e7802dd 100644 --- a/internal/config/validation_test.go +++ b/internal/config/validation_test.go @@ -956,6 +956,7 @@ func TestValidateAuthConfig(t *testing.T) { server *StdinServerConfig shouldErr bool errMsg string + clearEnv bool // when true, ensure ACTIONS_ID_TOKEN_REQUEST_URL is unset }{ { name: "valid github-oidc auth on http server", @@ -1016,10 +1017,30 @@ func TestValidateAuthConfig(t *testing.T) { shouldErr: true, errMsg: "type", }, + { + name: "github-oidc rejected when ACTIONS_ID_TOKEN_REQUEST_URL is not set", + server: &StdinServerConfig{ + Type: "http", + URL: "https://example.com/mcp", + Auth: &AuthConfig{ + Type: "github-oidc", + Audience: "https://example.com", + }, + }, + shouldErr: true, + errMsg: "ACTIONS_ID_TOKEN_REQUEST_URL", + clearEnv: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.clearEnv { + t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", "") + } else { + // Ensure OIDC env var is set for tests that expect valid config + t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", "https://token.actions.example.com") + } err := validateStandardServerConfig("test-server", tt.server, "mcpServers.test-server") if tt.shouldErr { require.Error(t, err) diff --git a/internal/launcher/launcher.go b/internal/launcher/launcher.go index 4f90bab65..b382c69b7 100644 --- a/internal/launcher/launcher.go +++ b/internal/launcher/launcher.go @@ -72,13 +72,18 @@ func New(ctx context.Context, cfg *config.Config) *Launcher { logger.LogInfo("startup", "GitHub Actions OIDC provider initialized") } - // Validate that all servers requiring OIDC have the provider available + // Pre-populate serverErrors for OIDC-misconfigured servers so /health + // reports them as "error" immediately at startup, not only after the + // first failed request. + serverErrors := make(map[string]string) for serverID, serverCfg := range cfg.Servers { if serverCfg.Auth != nil && serverCfg.Auth.Type == "github-oidc" && oidcProvider == nil { - logger.LogError("startup", + errMsg := fmt.Sprintf( "Server %q requires OIDC authentication but ACTIONS_ID_TOKEN_REQUEST_URL is not set. "+ "OIDC auth is only available when running in GitHub Actions with `permissions: { id-token: write }`.", serverID) + logger.LogError("startup", "%s", errMsg) + serverErrors[serverID] = errMsg } } @@ -91,7 +96,7 @@ func New(ctx context.Context, cfg *config.Config) *Launcher { startupTimeout: startupTimeout, oidcProvider: oidcProvider, serverStartTimes: make(map[string]time.Time), - serverErrors: make(map[string]string), + serverErrors: serverErrors, } } diff --git a/internal/launcher/launcher_test.go b/internal/launcher/launcher_test.go index c3016e283..a0f731312 100644 --- a/internal/launcher/launcher_test.go +++ b/internal/launcher/launcher_test.go @@ -776,6 +776,32 @@ func TestLauncher_MixedAuthTypes(t *testing.T) { assert.Nil(t, l.oidcProvider, "OIDC provider should be nil without env vars") } +func TestLauncher_OIDCMissingEnvRecordsErrorAtStartup(t *testing.T) { + ctx := context.Background() + t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", "") + + cfg := &config.Config{ + Servers: map[string]*config.ServerConfig{ + "oidc-http": { + Type: "http", + URL: "https://secure.example.com/mcp", + Auth: &config.AuthConfig{ + Type: "github-oidc", + Audience: "https://secure.example.com", + }, + }, + }, + } + + l := New(ctx, cfg) + defer l.Close() + + // The launcher should record an error for the OIDC server at startup + state := l.GetServerState("oidc-http") + assert.Equal(t, "error", state.Status, "OIDC server with missing env should be in error state at startup") + assert.Contains(t, state.LastError, "ACTIONS_ID_TOKEN_REQUEST_URL") +} + func TestGetServerState_StoppedByDefault(t *testing.T) { cfg := newTestConfig(map[string]*config.ServerConfig{ "test-server": {Type: "stdio", Command: "echo"}, From e276b072557e688c1818a0c54c926c778fd06bba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 02:54:01 +0000 Subject: [PATCH 3/4] Update launcher comment to clarify defensive fallback nature of OIDC check Agent-Logs-Url: https://github.com/github/gh-aw-mcpg/sessions/b2ecfab2-61b7-47ed-b0ab-6a60c21cffd0 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- internal/launcher/launcher.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/launcher/launcher.go b/internal/launcher/launcher.go index b382c69b7..8ee9a7daf 100644 --- a/internal/launcher/launcher.go +++ b/internal/launcher/launcher.go @@ -72,9 +72,10 @@ func New(ctx context.Context, cfg *config.Config) *Launcher { logger.LogInfo("startup", "GitHub Actions OIDC provider initialized") } - // Pre-populate serverErrors for OIDC-misconfigured servers so /health - // reports them as "error" immediately at startup, not only after the - // first failed request. + // Defensive fallback: pre-populate serverErrors for OIDC-misconfigured servers + // so /health reports them as "error" immediately at startup. Normal config + // validation (validateAuthConfig) catches this earlier; this handles cases + // where validation was bypassed (e.g., tests constructing configs manually). serverErrors := make(map[string]string) for serverID, serverCfg := range cfg.Servers { if serverCfg.Auth != nil && serverCfg.Auth.Type == "github-oidc" && oidcProvider == nil { From 361f687d01f596109bb34260c9cbbec001090f8c Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Tue, 7 Apr 2026 20:30:00 -0700 Subject: [PATCH 4/4] Update internal/config/validation.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- internal/config/validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/validation.go b/internal/config/validation.go index acf6291c3..3d88af44a 100644 --- a/internal/config/validation.go +++ b/internal/config/validation.go @@ -264,7 +264,7 @@ func validateAuthConfig(auth *AuthConfig, serverName, jsonPath string) error { if os.Getenv("ACTIONS_ID_TOKEN_REQUEST_URL") == "" { logValidateServerFailed(serverName, "ACTIONS_ID_TOKEN_REQUEST_URL is not set") return rules.MissingRequired( - "ACTIONS_ID_TOKEN_REQUEST_URL", "environment", authPath, + "ACTIONS_ID_TOKEN_REQUEST_URL", "github-oidc", authPath, "Server requires OIDC authentication but ACTIONS_ID_TOKEN_REQUEST_URL is not set. "+ "OIDC auth requires running in GitHub Actions with `permissions: { id-token: write }`") }