From 9a2f317e46499e19e9a761a8d79bceeedc56a28c Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 11 Apr 2026 18:48:52 +0800 Subject: [PATCH] Support bridge auth token fallback for upstream ACP --- internal/acp/gateway_runtime.go | 4 +- internal/acp/provider_catalog.go | 13 +++-- internal/acp/providers_sync_test.go | 78 +++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/internal/acp/gateway_runtime.go b/internal/acp/gateway_runtime.go index 4d6727b..25d3d0b 100644 --- a/internal/acp/gateway_runtime.go +++ b/internal/acp/gateway_runtime.go @@ -79,9 +79,7 @@ func applyProductionGatewayRouting( Port: 443, TLS: true, } - request.Auth.Token = strings.TrimSpace( - shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", ""), - ) + request.Auth.Token = strings.TrimSpace(bridgeUpstreamAuthorizationHeader()) request.Auth.Password = "" request.ConnectAuthMode = "shared-token" request.ConnectAuthFields = []string{"token"} diff --git a/internal/acp/provider_catalog.go b/internal/acp/provider_catalog.go index 9c8c610..f475343 100644 --- a/internal/acp/provider_catalog.go +++ b/internal/acp/provider_catalog.go @@ -22,10 +22,17 @@ type syncedProvider struct { Enabled bool } -func newProductionProviderCatalog() (map[string]syncedProvider, []string) { - authorizationHeader := normalizeAuthorizationHeader( - strings.TrimSpace(shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", "")), +func bridgeUpstreamAuthorizationHeader() string { + return normalizeAuthorizationHeader( + firstNonEmptyString( + strings.TrimSpace(shared.EnvOrDefault("INTERNAL_SERVICE_TOKEN", "")), + strings.TrimSpace(shared.EnvOrDefault("BRIDGE_AUTH_TOKEN", "")), + ), ) +} + +func newProductionProviderCatalog() (map[string]syncedProvider, []string) { + authorizationHeader := bridgeUpstreamAuthorizationHeader() providers := []syncedProvider{ { ProviderID: "codex", diff --git a/internal/acp/providers_sync_test.go b/internal/acp/providers_sync_test.go index 70ded50..2b9bc59 100644 --- a/internal/acp/providers_sync_test.go +++ b/internal/acp/providers_sync_test.go @@ -70,6 +70,25 @@ func TestCapabilitiesExposeBuiltInProductionProviderCatalog(t *testing.T) { } } +func TestProductionProviderCatalogFallsBackToBridgeAuthToken(t *testing.T) { + t.Setenv("INTERNAL_SERVICE_TOKEN", "") + t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-auth-token") + + catalog, order := newProductionProviderCatalog() + if len(order) != 3 { + t.Fatalf("expected 3 providers in order, got %#v", order) + } + for _, providerID := range order { + provider, ok := catalog[providerID] + if !ok { + t.Fatalf("expected provider %q in catalog", providerID) + } + if got := provider.AuthorizationHeader; got != "Bearer bridge-auth-token" { + t.Fatalf("expected fallback bearer header for %q, got %q", providerID, got) + } + } +} + func TestProvidersSyncMethodIsRemovedFromProductionFlow(t *testing.T) { server := NewServer() _, rpcErr := server.handleRequest(shared.RPCRequest{ @@ -165,6 +184,65 @@ func TestExecuteSessionTaskUsesBuiltInProductionProvider(t *testing.T) { } } +func TestExecuteSessionTaskUsesBridgeAuthTokenFallbackForBuiltInProvider(t *testing.T) { + externalServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got := r.Header.Get("Authorization"); got != "Bearer bridge-auth-token" { + t.Fatalf("expected fallback bearer auth header, got %q", got) + } + defer func() { + _ = r.Body.Close() + }() + var request map[string]any + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + t.Fatalf("decode request: %v", err) + } + _ = json.NewEncoder(w).Encode(map[string]any{ + "jsonrpc": "2.0", + "id": request["id"], + "result": map[string]any{ + "success": true, + "output": "bridge-auth-token-ok", + }, + }) + })) + defer externalServer.Close() + + t.Setenv("INTERNAL_SERVICE_TOKEN", "") + t.Setenv("BRIDGE_AUTH_TOKEN", "bridge-auth-token") + + server := NewServer() + setTestBridgeProvider(server, syncedProvider{ + ProviderID: "codex", + Label: "Codex", + Endpoint: externalServer.URL, + AuthorizationHeader: bridgeUpstreamAuthorizationHeader(), + Enabled: true, + }) + + response, rpcErr := server.executeSessionTask(task{ + req: shared.RPCRequest{ + Method: "session.start", + Params: map[string]any{ + "sessionId": "session-bridge-auth-fallback", + "threadId": "thread-bridge-auth-fallback", + "taskPrompt": "hello from bridge auth fallback", + "workingDirectory": t.TempDir(), + "routing": map[string]any{ + "routingMode": "explicit", + "explicitExecutionTarget": "singleAgent", + "explicitProviderId": "codex", + }, + }, + }, + }) + if rpcErr != nil { + t.Fatalf("expected success, got rpc error: %v", rpcErr) + } + if got := response["output"]; got != "bridge-auth-token-ok" { + t.Fatalf("expected fallback provider output, got %#v", response) + } +} + func TestExecuteSessionTaskEnrichesExternalProviderResultWithArtifactsAndRemoteMetadata(t *testing.T) { workingDir := t.TempDir() if err := os.MkdirAll(filepath.Join(workingDir, "outputs"), 0o755); err != nil {