Skip to content

fix: ensure user identity is propagated across A2A requests/sessions#1775

Open
onematchfox wants to merge 4 commits intokagent-dev:mainfrom
onematchfox:fix-a2a-user
Open

fix: ensure user identity is propagated across A2A requests/sessions#1775
onematchfox wants to merge 4 commits intokagent-dev:mainfrom
onematchfox:fix-a2a-user

Conversation

@onematchfox
Copy link
Copy Markdown
Contributor

@onematchfox onematchfox commented Apr 29, 2026

Ensures that caller identity correctly propagates from controller->agent->controller.

Addresses #1293 (comment) and potentially also #1771

Copilot AI review requested due to automatic review settings April 29, 2026 08:16
@github-actions github-actions Bot added the bug Something isn't working label Apr 29, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes identity propagation for proxy-based authentication by ensuring the controller forwards the authenticated user ID to downstream A2A runtimes via the X-User-Id header (in addition to forwarding Authorization when present), aligning behavior with UnsecureAuthenticator.UpstreamAuth.

Changes:

  • Update ProxyAuthenticator.UpstreamAuth to set X-User-Id from the authenticated session principal.
  • Extend TestProxyAuthenticator_UpstreamAuth to assert X-User-Id forwarding.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
go/core/internal/httpserver/auth/proxy_authn.go Forward X-User-Id (and Authorization when available) for upstream A2A requests under proxy auth.
go/core/internal/httpserver/auth/proxy_authn_test.go Add test assertion ensuring X-User-Id is forwarded in UpstreamAuth.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@onematchfox onematchfox marked this pull request as draft April 29, 2026 13:30
@onematchfox
Copy link
Copy Markdown
Contributor Author

Seeing some unexpected behaviour when running this change in a live environment (that I didn't see in testing) - may need some additional changes.so reverting to draft for now until I understand what's going on.

Without this, downstream A2A runtimes fall back to deriving the caller
identity from the context ID (`A2A_USER_<contextID>`) instead of the
real authenticated user, because `UserIDCallInterceptor` reads `X-User-Id`
rather than the forwarded `Bearer` token.

Mirrors the behaviour of [`UnsecureAuthenticator.UpstreamAuth`](https://github.com/kagent-dev/kagent/blob/b50661f858787f6c4de2a73479f492ba82af6ca4/go/core/internal/httpserver/auth/authn.go#L48) which
already sets this header.

Addresses kagent-dev#1293 (comment)

Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
@onematchfox onematchfox force-pushed the fix-a2a-user branch 3 times, most recently from 66ee32e to 6750b68 Compare April 30, 2026 08:16
@onematchfox
Copy link
Copy Markdown
Contributor Author

FYI @supreme-gg-gg, I think I might just end up fixing #1771 as part of this

@onematchfox onematchfox changed the title fix(controller): forward X-User-Id in ProxyAuthenticator.UpstreamAuth fix: ensure user identity is propagated across A2A requests/sessions Apr 30, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Apr 30, 2026
Previously, agent calls authenticated with a k8s SA Bearer token were attributed
to the SA identity (e.g. system:serviceaccount:ns:name) instead of the real caller.
This caused sessions to be scoped to the wrong identity.

Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Go (ADK executor):
- Extend TokenRoundTripper to inject X-User-Id from Go context on every
  outgoing request
- Wire the authenticated user ID into the Go context in A2aAgentExecutor
  after extracting it from the call context

Python (ADK):
- Add a ContextVar in kagent-core to carry the caller's user ID for the
  duration of an async request task
- Set it once in KAgentRequestContextBuilder.build() so it is available
  before any downstream HTTP calls are made
- Read it in KAgentTokenService._add_headers so every outgoing
  httpx request automatically carries X-User-Id
- Remove the redundant per-call X-User-ID headers from KAgentSessionService
  now that the event hook handles injection uniformly

Should fix kagent-dev#1771 as well.

Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
Prevents unwanted breakage when rolling this out as the new controller won't reject calls from existing agents. May also be desirable in situations were users call A2A endpoints on agents directly (bypassing oauth2-proxy and the controller).

Signed-off-by: Brian Fox <878612+onematchfox@users.noreply.github.com>
}
tokenString, ok := strings.CutPrefix(authHeader, "Bearer ")
if !ok {
return nil, ErrUnauthenticated
Copy link
Copy Markdown
Contributor Author

@onematchfox onematchfox Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a change in behaviour as it requires that a Bearer token be provided whereas the original code allowed any caller that knew the X-Agent-Name and X-User-Id to bypass use of a Bearer token. By default any agent created via the Agent CRD will supply a Bearer token - so this would only affect direct consumers of the API that are currently relying on this insecurity.

@onematchfox onematchfox marked this pull request as ready for review April 30, 2026 13:01
@onematchfox onematchfox requested a review from EItanya as a code owner April 30, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants