Scenario 06 · adds internal call context
The request fans out inside your own walls
One agent request arrives at your edge and becomes eleven internal service calls, three queue messages and a batch job that runs an hour later. The access token was audience-bound to the edge and shouldn't travel further. Everything downstream still needs to know it's acting for a specific person, under a specific authorization, in a specific request.
The two things people do instead
Forward the access token everywhere
Simple, and it does preserve the user's identity. It also means every internal service now holds a credential that works against your external API — and, if it's a token from scenario 05, possibly against a partner's. A compromised log shipper or a mis-scoped debug endpoint becomes an authenticated caller with the user's full external authority. The token's audience said "the edge" for a reason.
The same mistake has a second form, one hop out, and it's worth naming separately because it doesn't feel internal at all: a tool forwarding the token it was handed. An agent calls your tool with an access token; your tool needs to reach the service behind it; the token is right there. Passing it along makes the service downstream a replay target for a credential minted for you, and it means a leak anywhere in that chain is a leak of the caller's authority rather than your own. Exchange it — token exchange or a transaction token inside your domain, identity chaining when the service sits behind a different authorization server. This applies whether the tool is a plain API or an agent someone else is calling.
Drop the user and trust the network
Services authenticate to each other with their own credentials and pass a user_id in a header
or a request body. Every service is now trusting every other service's claim about who asked, with no
cryptographic backing. The audit trail says the orders service did it. When something goes wrong you cannot
reconstruct what a person actually authorized.
This was always a bit uncomfortable and mostly survivable, because the fan-out was deterministic — you could read the code and know what a request would touch. An agent-initiated request doesn't have that property. The set of services a task reaches depends on what the agent decided, which depends on input you didn't write. "Trust the network" stops being a shortcut and starts being the whole security model.
The mechanism
A credential scoped to one call chain
Transaction Tokens put a deliberate boundary at the edge of your trust domain. The external token stops there. In its place, the edge calls a Transaction Token Service — a Token Exchange endpoint — and gets back a short-lived token that is valid only inside, and only for this request.
The important property is that part of it is frozen:
- Immutable context. Who the user is, which agent is acting, what was authorized, what request this is. Set once at the edge, carried through every hop, unchangeable by anything downstream.
- A transaction identifier. One value tying all eleven calls, three messages and the batch job together. Your traces and your audit log finally describe the same thing.
- Per-hop requester context. Each service can record what it's asking for without touching the frozen part.
- A very short life. Minutes. It's scoped to one request, and requests end.
// a transaction token, decoded
{
"iss": "https://txn-token-service.internal",
"aud": "https://internal.example", // inside only. useless outside.
"txn": "txn_01KAB93QF7X2", // ties the whole fan-out together
"sub": "user:9f3c21",
"purp": "process_return",
"exp": 1787894700,
"tctx": { // immutable — set at the edge, frozen
"actor": { "sub": "agent:support-agent/inst-7741" },
"client_id": "https://agent.example.com/clients/support-agent",
"approved_by": "user:31f0aa", // from scenario 04's challenge
"challenge_id": "chal_01K9M2X7"
},
"azd": { // what was actually authorized
"type": "refund",
"order_id": "ORD-88213",
"amount": { "currency": "GBP", "amount": "900.00" }
},
"rctx": { "req_ip": "203.0.113.44", "authn": "mfa" }
}
Notice what came through from four rungs down. The agent instance from scenario 00's key. The user from scenario 01. The authorization details from scenario 03. The approval from scenario 04. By the time the payments service — service number nine, which has never heard of an agent — makes its decision, it has the entire story in a signed token it can verify locally.
The flow
Edge to service nine
-
Agent→Edge / API gateway
Arrives with a DPoP-bound access token, audience-restricted to the edge. The edge validates it and does not pass it on.
-
Edge→Txn Token Service
Token exchange: hands over the external token and the request context, receives a transaction token whose immutable context is now fixed for the life of this request.
-
Edge→Orders service
Calls inward, presenting the transaction token. Orders validates the signature locally — no callback to an authorization server on the hot path.
-
Orders→Txn Token Service
Needs to call two more services. Requests a token for the next hop; the service can add its own requester context but cannot alter
tctx,suborazd. -
Orders→Payments service
Service nine sees the user, the agent instance, the approved refund and the approver — and enforces its own policy on all four.
-
Payments→Partner API
Leaving the domain again: the transaction token becomes the input to another exchange, producing an outbound grant with internal detail stripped out. Scenario 05, in reverse.
The transaction token chaining profile covers that last step, including claims minimization — the internal token's structure, service names and request context never cross back over the boundary. Your internal topology stays internal.
And then turning it off
Short lifetimes are why this design degrades gracefully: a transaction token expires in minutes, so a compromised internal service has a small window and no external reach. But "revoke this agent everywhere, now" spans the IdP, several authorization servers and possibly a partner, and no token lifetime is short enough to feel like an answer during an incident.
The machinery for that is the Shared Signals Framework, with CAEP and RISC as its profiles: a standard way for one party to tell another that a session ended, a credential changed, risk was elevated, or a grant was revoked, in near real time. Every participant here can subscribe — the agent, the edge, the tool, the internal services — and what they owe in return is enforcement. A service that receives a revocation signal and keeps honouring a cached decision until it expires has turned a five-second problem back into a five-minute one.
This is the biggest operational gap on the site, and it's worth being precise about which kind of gap it is. It isn't a design problem and it isn't an open question — the specs exist, they're profiled for interoperability, and they're increasingly written down as a requirement rather than a nice-to-have. It's that not enough parties have implemented their half yet.
Until it's everywhere: short tokens, rotated refresh tokens, and the refresh token and authorization expiration draft so an agent knows when its grant ends rather than discovering it.
The whole ladder, in one token
Seven scenarios, each adding one piece, and the last one shows why they compose: the transaction token at service nine contains the agent's identity, the user's identity, the specific operation, the human approval and the request that occasioned it. Every one of those came from a different spec, most of them published years before anyone said "agentic," and none of them had to be redesigned to fit together.
That's the argument. Not that this is finished — it isn't — but that the remaining work is a handful of drafts landing in an ecosystem that already has the hard parts built.