Skip to content

STS token propagation: mint from the agent's own ServiceAccount on M2M runs (no human subject) #2071

Description

@QuentinBisson

Summary

ADKTokenPropagationPlugin (agentsts) does an RFC 8693 exchange with subject=<human token from session state> + actor=<agent ServiceAccount token> when a human token is present. On an autonomous (machine-to-machine) run with no human token, before_run_callback returns early (python/packages/agentsts-adk/src/agentsts/adk/_base.py:178-180) and mints nothing, so the downstream MCP request falls back to whatever static Authorization the RemoteMCPServer carries (headersFrom).

Request: add an opt-in mode where, when there is no human subject token, the plugin uses the agent's own pod ServiceAccount token as the subject (no actor), so the agent still presents its own identity to the MCP server on M2M.

Why

To give each agent a distinct identity to an MCP server / gateway on M2M today, operators must provision, per agent: a headersFrom Secret, a token-mint Job + refresh CronJob that mint that agent's SA token, and a dedicated RemoteMCPServer (because headersFrom is per-server, and a projected SA-token volume only mints for its own pod's SA). That is significant per-agent boilerplate and RemoteMCPServer sprawl, purely because the runtime won't forward the agent's own pod SA token on an M2M run.

The agent pod already runs as its own ServiceAccount and already mounts a projected token (the same one used as the OBO actor_token, read from SERVICE_ACCOUNT_TOKEN_PATH in agentsts-core). Letting the plugin present that on M2M removes the per-agent token-mint machinery entirely: one shared RemoteMCPServer, no headersFrom, every agent authenticating as its own pod SA.

Proposed behavior

In before_run_callback, when get_subject_token(...) yields nothing (M2M):

  • if the new option is enabled, set subject_token = <agent pod SA token> (from agentsts-core _actor_service / SERVICE_ACCOUNT_TOKEN_PATH), actor_token = None, and run the exchange (or forward directly, per STS config);
  • else keep current behavior (return None).
Run subject_token actor_token
OBO (human present) human token agent pod SA (unchanged)
M2M (new, opt-in) agent pod SA none

Compatibility / scope

  • Opt-in (new flag/option); default behavior unchanged → no regression for existing static-header setups.
  • Audience: the pod SA token must be projected with the STS/gateway audience; document that the agent's projected-token audience must match, or have the plugin request the configured audience.
  • Files: python/packages/agentsts-adk/src/agentsts/adk/_base.py (before_run_callback, ~152-209), python/packages/agentsts-core/.../_actor_service.py (pod SA token read), and the STS_WELL_KNOWN_URI wiring in go/core/pkg/env/kagent.go.

Interim (downstream)

We currently work around this with per-agent identity in our Helm chart (chart-owned SA + headersFrom Secret + token Job/CronJob per agent). That bridge works but is exactly the machinery this change would retire.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions