An agent presents two credentials: one representing me and one identifying Agent XYZ. A token service returns an access token stating that Agent XYZ is acting on my behalf.
What prevents the agent from taking that token somewhere I never authorized?
Not the actor claim alone. Secure delegation requires verified input credentials, controlled issuance, restricted output tokens, and enforcement by the receiving application. OAuth token exchange supplies the protocol; it does not automatically determine what I intended.
The RFC behind token exchange
RFC 8693, OAuth 2.0 Token Exchange, defines an OAuth-based Security Token Service, or STS. A client submits existing security tokens and requests a new token suitable for a particular purpose. The exchange can change token format, target audience, or authorization context, including representing delegation or impersonation.
The distinction matters:
- Impersonation: the recipient sees the subject’s identity without necessarily distinguishing the intermediary.
- Delegation: the token preserves the subject and identifies the party acting for that subject.
For our agent scenario, delegation is the useful model: Lisa remains the authorization subject, while Agent XYZ is recorded as the actor.
How an exchange works
An illustrative request to an STS token endpoint contains:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=<Lisa’s eligible access token>
subject_token_type=urn:ietf:params:oauth:token-type:access_token
actor_token=<Agent XYZ’s supported identity token>
actor_token_type=urn:ietf:params:oauth:token-type:jwt
requested_token_type=urn:ietf:params:oauth:token-type:access_token
audience=case-management-api
scope=cases.read cases.update
The subject_token provides the subject’s security context; the optional actor_token identifies the proposed actor. audience, resource, and scope describe requested targets and permissions. These are requests, not issuer obligations. Client authentication is a separate consideration from submitting either token.
The conceptual flow is:
Existing credentials + requested target and permissions
|
v
Security Token Service
├─ Validate credentials
├─ Evaluate exchange policy
├─ Establish delegated authority
└─ Issue a constrained token
|
v
Resource API
├─ Validate issued token
└─ Authorize this operation
An exchange bridges security contexts; it is not simply a JWT re-signing operation. RFC 8693 explicitly limits its scope to the exchange protocol rather than defining every deployment’s trust and authorization policy.
The exchanger must verify—not believe—the inputs
The STS should not accept:
subject = Lisa
actor = Agent XYZ
trust_me = true
It must establish those identities from credentials validated under an explicitly configured trust relationship. Token-validation rules depend on the accepted token profile; the API must also validate the resulting token independently. For JWT access tokens using RFC 9068, validation includes the expected token type, issuer, audience, signature, and expiration. (datatracker.ietf.org)
For this deployment, I would require the following issuance controls:
| Boundary | Required decision |
| Input trust | Is this issuer permitted to supply this kind of identity evidence? |
| Credential validity | Is the token authentic, current, and acceptable in this exchange context? |
| Actor identity | Which actor does the verified credential actually identify? |
| Exchange eligibility | May this client or actor exchange this subject’s credential? |
| Target authorization | May this delegation reach the requested API? |
| Permission authorization | Which requested operations may the output token permit? |
| Output binding | Must the resulting token be bound to a particular presenting key? |
These are recommended policy requirements—not defaults guaranteed by enabling RFC 8693. Its may_act claim can express actor eligibility, but identifying an eligible actor is still different from authorizing every requested target or operation.
Crucially, the resulting token is normally issued for the resource server to validate, not merely for the exchanger to recognize later. A resource server that trusts the STS must still enforce the token’s intended use.
A signed actor claim is attribution, not consent
An illustrative delegated token might contain:
{
“iss”: “https://identity.example.org”,
“sub”: “user:lisa”,
“aud”: “case-management-api”,
“scope”: “cases.read cases.update”,
“act”: {
“sub”: “spiffe://agents.example.org/agent/xyz”
}
}
The issuer asserts that Lisa is the subject and Agent XYZ is the current actor. The signature protects that assertion against modification. The actor claim does not, by itself, establish that Lisa approved a specific case update or requested access to another application.
This is the central distinction:
Who is acting, whose authority applies, and which action was authorized are separate facts.
If the deployment intentionally gives Agent XYZ all of Lisa’s application rights, then an unexpected operation within those rights may be policy-authorized even though Lisa did not intend it. No token format can recover a narrower instruction that the authorization system never captured.
Preventing the “Lisa never asked for that” failure
There are several different failures hiding inside that example. They require different controls.
- Using the token at another application
Suppose Agent XYZ receives a token for the case-management API, then presents it to payroll.
Payroll must reject it because payroll is not its intended audience. Sharing an issuer or signing key does not make a token valid for every application. RFC 9700 recommends audience-restricted access tokens to limit this kind of reuse.
For this architecture, prefer one narrowly defined target per token and exact audience validation at each API.
- Exchanging it again for an unauthorized application
Audience validation does not help if the agent can ask an STS to convert its case-management token into a payroll token.
The recommended issuance policy is to authorize each permitted exchange edge explicitly:
Lisa delegation + Agent XYZ → case-management: allowed
Lisa delegation + Agent XYZ → payroll: denied
Changing audience is a legitimate purpose of exchange, so the defense is not “never change audience.” It is never let possession of an input token alone authorize an arbitrary audience transition. This follows from the protocol’s separation between the requested target and issuer policy.
- Performing an unintended action within the correct API
Audience restriction cannot distinguish “update case 4821” from “update every case Lisa can access.” A broad cases.update scope may be equally insufficient.
RFC 9396, Rich Authorization Requests, defines authorization_details for expressing finer-grained authorization requirements using structured JSON. The application-specific authorization type must define what those details mean and how they are enforced.
For example, a proposed—not standardized—authorization object could be:
{
“type”: “case_operation”,
“actions”: [“update”],
“case_ids”: [“4821”],
“allowed_fields”: [“status”],
“allowed_status_values”: [“resolved”]
}
In this design, Lisa approves that operation through a trusted authorization interaction. The STS issues authority no broader than the approved operation, and the API checks the actual resource and requested mutation against it.
RFC 9396 does not automatically add this object to RFC 8693 exchanges. Supporting it requires an agreed issuer/API profile and enforcement implementation. Structured authorization is useful only when the resource server understands and checks it.
- Stealing and replaying the token
A bearer token can be used by whoever possesses it. A signed act claim can therefore attribute a stolen token to Agent XYZ even when another party presents it.
Sender-constrained tokens address that problem by requiring proof of possession of an associated key. RFC 9700 recommends these alongside audience restrictions.
RFC 9449, DPoP, provides application-layer sender constraint. The client supplies signed proofs, and the recipient validates the proof and its relationship to the access token.
DPoP proofs include request method and URI information, token binding through ath for protected-resource requests, and freshness/replay controls. But DPoP does not ordinarily bind the business request body or establish Lisa’s approval of its contents.
A legitimate—or compromised—agent holding the bound key can still generate proofs for operations its token permits. Sender constraint prevents a different presenter from simply replaying the token; it does not make the authorized presenter trustworthy.
A practical task-bound delegation design
For the Lisa/Agent XYZ scenario, I would implement the following:
- Capture approval outside the agent’s assertions. Store an approved delegation identifying Lisa, Agent XYZ, target application, operations, resources, and expiry.
- Require that approval during exchange. Validate both credentials and check the requested output against the stored grant.
- Issue narrowly. Restrict audience, operations, object identifiers, lifetime, and presenting key where supported.
- Enforce at the API. Validate the token, then compare the actual operation and affected object with the grant.
- Control further exchange. Deny unapproved target transitions and privilege expansion.
- Record execution. Log subject, actor, delegation identifier, target, resource, authorization decision, and outcome.
- Use state where necessary. For one-time approval, atomically consume the authorization at the operation boundary; a unique token identifier alone does not make a token single-use.
This is a proposed deployment architecture combining token exchange, constrained access tokens, and fine-grained authorization—not a claim that RFC 8693 implements these controls automatically.
The takeaway
Token exchange can turn verified credentials into a token trusted by another system. It must not turn identity evidence into unrestricted authority.
For the threat in question, insist on four distinct properties:
- Verified subject and actor.
- Authorized issuance for the requested target.
- Permissions bounded by the approved delegation.
- Resource-server enforcement of those bounds.
“Agent XYZ acting for Lisa” explains attribution. “Agent XYZ may perform this operation on this resource for Lisa” is the authorization boundary.