RFC 8693 is suddenly the most-cited OAuth RFC nobody read
Token Exchange existed for years as a niche federation primitive. Agent systems made it load-bearing. A practical walk through what it does and where the sharp edges are.
RFC 8693 — "OAuth 2.0 Token Exchange" — published in 2020, sat quietly for years. It defines a grant_type=urn:ietf:params:oauth:grant-type:token-exchange flow where a client can take one token and exchange it for another, optionally with narrower scopes, a different audience, or different actor semantics.
It was designed for federation: an enterprise STS handing out tokens that downstream services could exchange for service-specific credentials. It found its real purpose with agents.
Why agents need it
The shape of the agent problem:
- A user authenticates to your app (Okta, say). You get an ID token.
- The user asks your agent to do something that touches GitHub, Slack, and an internal DB.
- The agent needs to act on behalf of the user against those services — but with a credential that names the agent and the task, not the raw user session.
OAuth 2.0 alone doesn't have a primitive for "narrow this token I have into something more specific." You either re-prompt the user for consent at every service (UX disaster) or you cache long-lived broad-scope credentials (security disaster). RFC 8693 is the third option.
The shape of the call
A token-exchange request looks like:
POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOi... // the user's ID token
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&actor_token=eyJhbGciOi... // the agent's own credential (optional)
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&resource=https://api.github.com
&scope=repo:status repo:read
&audience=github
The interesting fields:
subject_token— whose request is this. The user.actor_token— who is making it. The agent. When present, the issuer produces a token with theactclaim (§4.1 of the RFC) embedding the actor's identity inside the token of the subject.resource+audience— narrow the audience. RFC 8707 resource indicators belong here.scope— narrow the scope.
The output is a new token: still on behalf of the user, but now naming the agent as the actor, scoped to repo:read, and audience-bound to GitHub.
The act claim is the load-bearing detail
If you read one section of the RFC, read §4.1. The act claim is what lets downstream services see both "this was for user Alice" and "this was the release-bot agent acting." Without it, agent actions look like user actions in the audit log — fine until something goes wrong and you need to ask which agent invoked the call.
The claim is nestable. An agent calling a sub-agent produces a token with act containing act — the full chain is recoverable from the credential alone.
Sharp edges
Three things to know before you ship this:
1. Not every authorization server supports it. Okta added support for token exchange relatively recently and there are still gaps. Auth0 has it. Azure AD has its own On-Behalf-Of flow that's similar but not RFC 8693. If you need to support multiple IdPs, your abstraction has to deal with this.
2. The exchange policy is yours to write. The RFC defines the protocol, not what's allowed. Whether release-bot can exchange Alice's token for a GitHub repo:admin scope is a policy decision your authorization server has to make. Most STS implementations expose this as a config DSL — write it carefully because it's the thing standing between Alice's session and your production tooling.
3. Refresh semantics are unspecified. The RFC doesn't say what happens when the original subject token expires. The standard answer is "the exchanged token's lifetime should not exceed the subject token's," but enforcement is up to your server. If you let exchanged tokens live longer than their subject token, you've created a privilege-escalation path: an agent holding a stale exchanged token after the user's session ended.
When you'd reach for it
If you're building anything where "an automated thing acts on behalf of a person against a downstream service," you'll eventually need this. Specifically:
- Multi-tenant SaaS where one customer's agent calls another vendor's API on their behalf.
- Internal platforms where engineers' coding agents push to GitHub as themselves.
- Any agent flow that needs
actto be visible in downstream audit logs.
If you're avoiding it, it's usually because (a) your IdP doesn't support it cleanly, or (b) you've decided your agents will use shared service accounts and accept the attribution loss. The first is solvable; the second is the thing to push back on.
RFC 8693 is one of those specs that seems esoteric until you have the problem, and then it's clearly the right answer. The work is in the exchange policy, not the protocol.
Want to talk to us? Request access or email the team.