fix: key STS token cache by acting subject, not session alone - #2459
Open
QuentinBisson wants to merge 4 commits into
Open
fix: key STS token cache by acting subject, not session alone#2459QuentinBisson wants to merge 4 commits into
QuentinBisson wants to merge 4 commits into
Conversation
Rebased onto current main; adapts to the RFC 8707 resource/audience constructor params and the a2a-go v2 CallContext API. Signed-off-by: QuentinBisson <quentin@giantswarm.io>
QuentinBisson
force-pushed
the
fix/sts-token-cache-per-subject-rebased
branch
from
August 17, 2026 13:08
ab5600f to
5503c06
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a cross-subject identity leak in the Go STS token-propagation plugin by ensuring exchanged (delegated) tokens are cached per (session, acting subject) rather than per session alone. This aligns token propagation with the shared-session model where different callers within the same session must not inherit each other’s delegated authority.
Changes:
- Re-keys the STS token cache from
sessionIDto a composite(sessionID, subject)key, wheresubjectis derived from the acting bearer’s issuer-scopedsub(or a hash fallback for opaque/sub-less tokens). - Ensures both the exchange path (
BeforeRunCallback) and MCP injection path (HeaderProvider) resolve the acting bearer/subject before cache lookup, including a CallContext fallback for transport-layer requests. - Updates cache cleanup behavior to sweep expired entries across all
(session, subject)entries and adds/updates unit tests for the new behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| go/adk/pkg/sts/plugin.go | Implements per-(session, subject) caching and acting-bearer recovery for both exchange and header injection paths. |
| go/adk/pkg/sts/plugin_test.go | Adds coverage for issuer-scoped subject partitioning, shared-session per-subject behavior, and CallContext-based bearer recovery. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Keying the cache by (session, subject) multiplies entries per session, so an entry whose token carries no exp claim now pins one slot per caller instead of one per session. Give those a bounded lifetime so every entry stays evictable. Track the earliest expiry so AfterRunCallback only walks the cache once something can actually be evicted, matching the Python plugin. An issuer-less token leaves sub unqualified; those partition by token hash rather than by a key two issuers could both produce. Signed-off-by: Quentin Bisson <quentin@giantswarm.io>
QuentinBisson
force-pushed
the
fix/sts-token-cache-per-subject-rebased
branch
from
August 17, 2026 19:46
74ccaaa to
7ac8cd3
Compare
An empty subject identifies no principal, so an entry stored under it would be shared by every credential-less caller in a session. The cache accessors now refuse it. Name the key's input for what it is, the credential the request authenticates with, rather than the caller's bearer specifically. Signed-off-by: Quentin Bisson <quentin@giantswarm.io>
QuentinBisson
force-pushed
the
fix/sts-token-cache-per-subject-rebased
branch
from
August 17, 2026 19:49
7ac8cd3 to
a63d354
Compare
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.
Supersedes #2317 (auto-closed by stale-bot; rebase onto current main hit real conflicts with #2106's resource/audience support, so GitHub would not let the original be reopened).
Ref #2181 (Go side; the Python counterpart #2460 carries the closing reference).
What
A session shared by multiple subjects (e.g. multiple users routed through the same A2A session) currently runs every caller's tool calls under whichever subject's exchanged token was cached first. The STS token cache was keyed only by session ID.
This keys the cache by
(sessionID, subject), wheresubjectis derived from the acting bearer token's issuer-scopedsubclaim (or a hash of the raw token for opaque/sub-less tokens). BothBeforeRunCallback(exchange path) andHeaderProvider(MCP header injection path) resolve the acting subject from the request's own bearer before doing the cache lookup, so each caller gets its own exchanged token.HeaderProvidernow also recovers the bearer from the A2ACallContextwhenmodels.BearerTokenKeyisn't threaded to the MCP request context, matching what the round-tripper's propagate-token path already reads.Cache lifetime
Keying by
(sessionID, subject)holds one entry per caller instead of one persession, so an entry whose token carries no
expclaim would pin a slot percaller for the lifetime of the process. Those entries now get a bounded
lifetime, which keeps every entry evictable.
AfterRunCallbacktracks the earliest expiry in the cache and only walks itonce something can actually be evicted, matching the Python plugin in #2460.
An absent
issleavessubunqualified, so issuer-less tokens partition bytoken hash rather than by a key that two issuers omitting
isscould bothproduce.
go-unit-testsis red onmainas well (CRD validation ingo/api/v1alpha2and
go/api/v1alpha3); it is not caused by this diff.