Repository navigation
Description
Background
cortex #1496 added token-exchange-aiac, an outbound token exchange with no routes. It derives the audience from the destination host (spiffe://<td>/ns/{namespace}/sa/{service} for operator-registered workloads) and requests no scope, so a target is granted to a caller only by a default client scope of the caller's client.
rossoctl/aiac#242 moves the AIAC demo's github-agent to that plugin and drops authproxy-routes. The agent calls the production GitHub tool of this repo's github-issue demo at MCP_URL=http://github-tool-mcp:9090/mcp. The route that used to map that host is gone:
- host: "github-tool-mcp"
target_audience: "github-tool"
token_scopes: "openid github-tool-aud github-full-access"Under token-exchange-aiac those calls are not exchanged: the derived audience spiffe://<td>/ns/team1/sa/github-tool-mcp names no Keycloak client, so the call passes through with the caller's token. And no token carries github-full-access, so the tool always uses its public PAT.
Why the demo does not fit the plugin
demos/github-issue/k8s/github-tool-deployment.yaml and demos/github-issue/setup_keycloak.py:
- No client the host derives. The tool's Service is
github-tool-mcp, but it runs under thedefaultServiceAccount, and its Keycloak client is a plaingithub-toolthatsetup_keycloak.pycreates by hand. The plugin's SPIFFE form needs Service name == ServiceAccount name == the last path segment of the client ID. - Not enrolled. The tool has no AgentRuntime CR and no AuthBridge sidecar, so the operator registers no client for it.
- Optional scopes.
setup_keycloak.pymakesgithub-tool-audandgithub-full-accessrealm optional scopes and links them to the agent as optional. The plugin sends noscope, so an optional scope is never applied. - The name
github-toolis taken in AIAC's demo. AIAC's UC-1 stub tool runs as Service = ServiceAccount =github-toolinteam1, so the production tool cannot take that name.
Task
Make the production tool reachable by token-exchange-aiac under its existing Service name github-tool-mcp, so that the AIAC agent's unchanged MCP_URL is exchanged:
- Give the tool Deployment a ServiceAccount named
github-tool-mcp(equal to its Service), and enroll it (an AgentRuntime CR, typetool) so the operator registers the clientspiffe://<td>/ns/team1/sa/github-tool-mcpand its audience scopeagent-team1-github-tool-mcp-aud. - The tool reads only the scopes of the token today (
REQUIRED_SCOPE), so itsAUDIENCEenv is unused; set it to the new client ID or drop it, and say which in the manifest. - In
setup_keycloak.py, stop creating the hand-madegithub-toolclient andgithub-tool-audscope for this path, and makegithub-full-accessgrantable as a default scope of the caller (not a realm default, so it does not reach every client). - Decide how the alice/bob split works without a requested
scope. With default scopes, every token the agent exchanges carriesgithub-full-access; the demo currently gives it only to a token that requests it. - Update
demo-ui.md,demo.mdanddemo-aiac.md(they describe the route and the optional scopes).
AIAC's side (rossoctl/aiac#242): its WIRE step links the tool's audience scope agent-team1-github-tool-mcp-aud as a default scope of github-agent when it exists, and the agent's MCP_URL already targets github-tool-mcp. It does not link github-full-access: as a default scope it would be in every token github-agent exchanges, so every user would get the privileged PAT. That is the open question of the alice/bob item above: token-exchange-aiac requests no scope, so a per-user grant needs another mechanism (for example a scope mapped from a user role, or the tool choosing the PAT from the user rather than a token scope).
Acceptance criteria
- With the tool deployed from this demo and AIAC's WIRE run, a
tools/callfrom the AIACgithub-agenttogithub-tool-mcp:9090/mcpis exchanged: the tool receives a token whoseaudisspiffe://<td>/ns/team1/sa/github-tool-mcp. - The privileged PAT is chosen per user, not per agent: a user granted full access gets it, others get the public PAT. A default
github-full-accesson the agent's client does not meet this. - No
authproxy-routesand no realm default scope are added. - The demo docs describe the new wiring.
Tracked on the AIAC side by rossoctl/aiac#247.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
fix: address the third review of the token-exchange-aiac switch
huang195 commentedon Oct 29, 2026
Closing for now: this is specific to the Kubernetes / envoy-sidecar / SPIRE deployment path. Current prioritization is the laptop install; K8s and envoy-sidecar remain supported targets but are not priority. Reopen when that work resumes.