feat: go runtime sts client and adk integration#1880
Conversation
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
There was a problem hiding this comment.
Pull request overview
Adds a Go implementation of OAuth 2.0 Token Exchange (RFC 8693) and wires it into the Go ADK runtime so MCP tool calls can automatically receive exchanged (or propagated) Authorization headers, with E2E coverage for both Python and Go runtimes.
Changes:
- Introduces a new
go/adk/pkg/stspackage (STS client, integration wrapper, token-propagation ADK plugin, and tests). - Extends MCP HTTP transport to support a per-request dynamic header provider (used for STS-exchanged tokens).
- Wires the STS plugin into the Go ADK runner/agent setup and expands E2E tests to validate Go runtime STS exchange behavior.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| go/go.mod | Adds JWT dependency used for parsing token expiry claims. |
| go/core/test/e2e/invoke_api_test.go | Runs STS integration E2E for both Python and Go runtimes and strengthens request assertions. |
| go/adk/pkg/sts/utils.go | Implements well-known discovery fetch + response normalization helpers. |
| go/adk/pkg/sts/plugin.go | Adds ADK plugin + header provider for per-session token exchange/propagation and caching. |
| go/adk/pkg/sts/plugin_test.go | Tests header injection and dynamic actor-token caching behavior. |
| go/adk/pkg/sts/models.go | Defines RFC 8693 request/response models and STS configuration types. |
| go/adk/pkg/sts/integration.go | Provides a higher-level integration wrapper around STS client + actor/subject token logic. |
| go/adk/pkg/sts/integration_test.go | Tests default subject-token extraction and actor-token caching behavior. |
| go/adk/pkg/sts/errors.go | Adds typed error structures for STS/config/network/exchange failures. |
| go/adk/pkg/sts/client.go | Implements the core STS token exchange client and request building. |
| go/adk/pkg/sts/client_test.go | Verifies request building, success paths, and error handling for STS client. |
| go/adk/pkg/sts/actor.go | Adds service-account token file reader for actor token retrieval. |
| go/adk/pkg/runner/adapter.go | Builds/enables the STS/token-propagation plugin via env and registers it with the ADK runner. |
| go/adk/pkg/mcp/registry.go | Adds DynamicHeaderProvider support and injects its headers into outbound MCP requests. |
| go/adk/pkg/mcp/registry_test.go | Tests precedence ordering between propagated/allowed/dynamic/static headers. |
| go/adk/pkg/agent/agent.go | Plumbs the STS plugin’s header provider into MCP toolset creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
| return nil, fmt.Errorf("failed to initialize STS integration: %w", err) | ||
| } | ||
|
|
||
| log.Info("Enabling STS token propagation plugin", |
There was a problem hiding this comment.
nit: could we format this log as one line?
| "", | ||
| nil, // fetchActorToken | ||
| nil, // getSubjectToken | ||
| 0, // default timeout |
There was a problem hiding this comment.
isn't the default time out 5s?
| } | ||
| } | ||
|
|
||
| func normalizeSTSConfig(config STSConfig) STSConfig { |
There was a problem hiding this comment.
what is the rationale behind this function?
| } | ||
|
|
||
| // Impersonate performs an impersonation token exchange (no actor token). | ||
| func (c *STSClient) Impersonate( |
There was a problem hiding this comment.
nit: the following functions are only used in tests but I do like them since they're very clear. I think it would be easier to follow if you used a switch statement in integration.go's ExchangeTokenWithActorToken function to invoke these two functions based on the presence of actorToken and actorTokenType
|
|
||
| srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| capturedAuth = r.Header.Get("Authorization") | ||
| capturedTrace = r.Header.Get("X-Trace-Id") |
There was a problem hiding this comment.
nit: x-trace-id here seems to be a bit of a misrepresentation given that our implementation only overwrites the Authorization header.
No description provided.