Module 14: Passthrough (forward the caller’s token, mint nothing)#
You are in the CPEX tutorial. This module needs the IdP.
Cookbook recipe: Recipe 4: Forward a token the caller already has.
Goal: recognize the case where the right move is to mint nothing, and instead validate the caller’s token and forward it unchanged.
The problem#
Sometimes the caller already holds a token that is correct for the downstream. In the agent-brokered case the agent authenticated to the IdP itself and handed CPEX a ready token, and the exchange modules 6, 12 and 13 perform would be pure overhead.
Passthrough is that case, defined by what is absent: there is no delegate(...) step. CPEX validates the token inbound, authorizes the call, and the token the caller presented is the token that flows on.
Build it#
There is nothing to add, only something to leave out: a plain resolver (as in module 2), a route that authorizes, and no delegator plugin at all. From policies/m14.yaml:
plugins:
- name: keycloak # validates the inbound token; that is its whole job
kind: identity/jwt
hooks: [identity.resolve]
config: { role: user, ... as in module 2 ... }
routes:
- tool: search_repos
authentication: [keycloak]
authorization:
pre_invocation:
- "require(authenticated)" # no delegate step, so the caller's token is forwarded| Shape | Step | What flows downstream |
|---|---|---|
| Passthrough (this module) | no delegate | the caller’s own token, unchanged |
| Mint (modules 6, 13) | delegate(subject: …) | a fresh token, scoped to one audience |
Run it#
cargo run -p cpex-tutorial --example m14_passthrough▸ alice → search_repos (validated and forwarded, no token minted)
✓ ALLOWED { ... "repositories":[ ... ] }
▸ anonymous → search_repos (no token to forward, require(authenticated) denies)
✗ DENIED [...] access deniedalice’s token was validated and the call went through with no exchange, so her token is what would reach the backend. The anonymous caller had no token to forward, so require(authenticated) denied it.
Passthrough is the cheapest option and the least contained: a forwarded token is as broad as it was issued, where a minted one is narrowed to a single audience. Forward only when the caller’s token is already scoped for the downstream. It is a security judgment, and in policy it is one line of difference: a delegate step, or none.
Try it#
- Add a mint step. Drop a
delegate(workday-oauth, target: workday-api, audience: workday-api, subject: user)beforerequire(authenticated)(and the delegator plugin from module 13). Now the route mints instead of forwarding: the same allow result, a narrower token downstream. - Break the audience. Point
audiences:at something the token doesn’t carry (e.g.[workday-api]) and re-run alice. Expect: inbound validation fails, because passthrough still requires a valid token, it just doesn’t exchange it.
Checkpoint#
How is this different from module 2?
When is passthrough the wrong choice?
Go deeper#
- Recipe 4: Forward a token the caller already has for the reference version and where it fits among the delegation subjects.
Next#
Module 15: Dual-principal: mint on behalf of a user while naming the agent that carried out the call.