Skip to content

[BUG] Legacy controller A2A passthrough drops authenticated effective user #2467

Description

@erauner12

📋 Prerequisites

  • I have searched the existing issues to avoid creating a duplicate
  • By submitting this issue, you agree to follow our Code of Conduct
  • I am using the latest version of the software
  • I have tried to clear cache/cookies or used incognito mode (if UI-related)
  • I can consistently reproduce this issue

🎯 Affected Service(s)

Controller Service

🚦 Impact/Severity

Blocker

🐛 Bug Description

The legacy controller A2A path authenticates the initiating caller and persists
tasks under that effective owner, but the controller-to-managed-runtime A2A hop
does not forward the effective user identity.

The upstream client interceptor constructs a new outbound request and preserves
provider authentication and tracing metadata, but it does not add x-user-id
from the controller's trusted authentication context. The managed runtime
therefore receives the request without the initiating user.

For a persisted input-required continuation, the runtime subsequently performs
an owner-scoped task lookup using its service identity rather than the task's
initiating owner. The existing task is then reported as not found, preventing a
standards-based client from continuing the same task safely.

This behavior is independent of whether the compatible legacy controller endpoint
is accessed with the A2A 0.3 or 1.0 wire because both versions use the same
controller-to-runtime hop.

Scope clarification

This issue covers only the legacy controller's outbound effective-user seam.
Related defects are tracked separately:

A persisted continuation requires both the controller outbound identity and the
runtime callback identity contracts to hold, but the two seams are independently
testable and reviewable.

🔄 Steps To Reproduce

  1. Deploy a maintained kagent release using the legacy controller A2A path with
    authenticated, owner-scoped task persistence enabled.
  2. Through the controller A2A endpoint, create a task that reaches
    input-required; retain its taskId and contextId.
  3. Confirm that the task is persisted under the authenticated initiating
    principal.
  4. From a later request authenticated as the same principal, send a continuation
    containing the retained taskId and contextId.
  5. Inspect the controller-to-runtime A2A request metadata and observe that the
    authenticated effective user is not forwarded as x-user-id.
  6. Observe the runtime's owner-scoped task lookup execute using its service
    identity and return Task not found for the existing task.
  7. Repeat through the legacy controller endpoint using A2A 0.3 and 1.0; both
    versions reproduce the same missing-identity hop.

The interceptor-level reproduction is deterministic: provide a request context
with an authenticated controller principal, run the legacy upstream client
interceptor, and inspect the resulting service parameters. Provider auth and
trace headers are present, while the effective user is absent.

🤔 Expected Behavior

The legacy controller should derive the effective user from trusted controller
authentication context and forward it to the managed runtime as x-user-id.

For ordinary requests, the effective user should be the authenticated session
principal. For a validated share context, it should be the share owner. Raw
inbound identity headers must not become an independent trust source.

The runtime should therefore receive the same effective owner used by the
controller's owner-scoped persistence boundary.

📱 Actual Behavior

The legacy controller's outbound A2A request contains provider authentication and
trace metadata but omits the effective user identity.

The managed runtime then performs persisted task resolution using its own service
identity. Because the task belongs to the authenticated initiating principal,
the owner-scoped lookup reports Task not found and the continuation fails.

💻 Environment

  • OS and version: Not environment-specific
  • Kubernetes version/provider: Not required for the source-level reproduction
  • Browser: Not applicable
  • Application version: reproduced with kagent v0.10.0-rc1
  • Affected component: legacy controller A2A client interceptor
  • Authentication: authenticated, owner-scoped task persistence
  • A2A versions tested through the compatible controller endpoint: 0.3 and 1.0

The legacy package remains present in current source, while the supported
current-main controller-v2 uses a different gateway path. This report does not
claim that controller-v2 uses the affected legacy outbound interceptor.

🔧 CLI Bug Report

Not attached because the live reproduction occurred in an internal Kubernetes
environment and generated diagnostics may contain infrastructure or
authentication details.

The source-level reproduction is deterministic. Reviewed and sanitized runtime
evidence can be provided if maintainers need additional diagnostics.

🔍 Additional Context

The required trust inputs already exist in the controller:

  • authenticated session context identifies the ordinary initiating principal;
  • validated share context identifies the effective share owner;
  • the controller-to-runtime request is already an authenticated internal hop;
  • runtime A2A handling already recognizes x-user-id request metadata.

The missing connection is adding the trusted effective user to the newly
constructed outbound request. The fix should not copy an arbitrary raw inbound
x-user-id value.

Focused regression coverage should verify:

  • authenticated-principal forwarding;
  • validated share-owner replacement of any preexisting value;
  • preservation of provider authentication and trace propagation; and
  • absence of identity synthesis when trusted controller context has no user.

Before the combined fixes, persisted continuation failed on all four compatible
create/resume wire combinations: 0.3→0.3, 0.3→1.0, 1.0→0.3, and
1.0→1.0.

📋 Logs

Controller persisted task owner: <authenticated-initiating-principal>
Controller → runtime service parameters: x-user-id absent
Runtime persisted-task callback identity: <runtime-service-identity>
Owner-scoped lookup: Task not found

📷 Screenshots

Not applicable.

🙋 Are you willing to contribute?

  • I am willing to submit a PR to fix this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions