Scope Narrowing and the OAuth Transaction Token Service
I recently came across the IETF OAuth Transaction Tokens draft specification(draft-08). It reminded me of a solution I had helped develop in my prior work. I also recall that similar solutions were organically developed in more than one financial institutions. So, I was excited to learn how the problem is solved.
Context
Modern computing architectures use multiple independently running workloads that are often invoked in order to process external API calls. These workloads run in isolated networks that can be compromised by attackers, leading to unauthorized actions such as: invocations without explicit transaction context, arbitrary client identity impersonation, parameter modification and theft of OAuth access tokens.
Transaction Tokens
Transaction Tokens (txn-tokens) are short-lived JWTs (JSON Web Token) designed to preserve caller identity, workload identity, and authorization context throughout a “Call Chain” within a Trust Domain. They are meant to prevent unauthorized invocations, parameter tampering, and token theft in multi-service architectures, problems anyone running microservices has encountered.
Scenario
Following diagram describes a scenario where txn-token is used to preserve caller identity and authorization context for a call on an external API endpoint deployed on an API Gateway.

Terms Used
Following terms are used in this post. They are listed verbatim from the spec.
Call Chain: The set of invocations across all workloads invoked to complete the requested transaction.
Trust Domain: A logical grouping of systems that share a common set of security controls and policies. In practice this may include a virtually or physically separated network, which contains two or more workloads. The workloads within a Trust Domain may be invoked only through published interfaces.
External Endpoint: A published interface to a Trust Domain that results in the invocation of a workload within the Trust Domain.
Transaction Token (Txn-Token): A signed JWT with a short lifetime, providing immutable information about the caller or workload, certain parameters of the call, and specific contextual attributes of the call. The Txn-Token is used to authorize subsequent calls in the Call Chain.
Transaction Token Service (TTS): A special service within the Trust Domain that issues Txn-Tokens to requesting workloads. Each Trust Domain using Txn-Tokens MUST have exactly one logical TTS.
Objectives
The authors of the spec are trying to achieve the following objectives.
Scope Narrowing
The spec mandates that Txn-Tokens scope should be “as narrowly defined as possible” and that the Transaction Token Service must ensure requested scope is equal to or less than the subject token’s scope. An example of a broad scope is api:delete that applies to delete action on any resource of the API while a scope such as account:delete only applies to the delete operation on the “account” resource of the API.
Short Token Lifetimes
Txn-Tokens are expected to be short-lived (on the order of minutes or less). A narrowly scoped, short-lived, transaction token reduces the attack surface of captured and replayed transaction tokens.
Transaction Traceability
The txn claim provides transaction traceability across the entire call chain. This will be invaluable for distributed tracing, debugging and root cause analysis.
Separation of Request and Transaction Contexts
The spec distinguishes between environmental context (IP address, auth method, etc.) and authorization details (parameters, computed values) using request context (rctx) and transaction context (tctx). This separation is good.
Scope Narrowing Requires Business Logic
In this post, I want to discuss issues behind scope narrowing, when performed by TTS.
The spec says the TTS “MUST ensure that the requested scope of the Txn-Token is equal or less than the scope(s) identified in the subject_token” (Section 14.6). It also says that scope should be “as narrowly defined as possible” for the transaction’s purpose. More specifically, “The values used for this claim are defined by the TTS as representative of the authorization model defined by the Trust Domain. The value may be literately and semantically different from, and represent an intent narrower, than a scope value issued to an external client.”
Question is how does the TTS know what scope is appropriate for a given business transaction?
Consider the following example.
External Request: scope=transfer:write (OAuth token)
Internal Transaction(s): "application wants to transfer USD 1,000,000.00 from one account to the other account after checking validity (good standing) of both the accounts, check transaction limits for each accounts according to their respective business policies"
Appropriate Txn-Token scope: ???
The TTS would need to maintain a mapping table like the following.
external_scope: transfer:create
transaction_type: check account standing
→ txn_scope: account:read
external_scope: transfer:create
transaction_type: check account limits
→ txn_scope: limits:read
external_scope: transfer:create
transaction_type: check business policies for accounts
→ txn_scope: business-policies:read
external_scope: transfer:create
transaction_type: transfer funds
→ txn_scope: transfer:create
external_scope: transfer:create
transaction_type: account credit
→ txn_scope: account:update
external_scope: transfer:create
transaction_type: account debit
→ txn_scope: account:update
This is business logic. The TTS now needs to understand application’s authorization model, which services need which permissions, and how to derive narrow scopes from broad external scope. This violates separation of concerns principle of design considering that TTS should be infrastructure, not an application-aware policy engine.
Architectural Choices
Here are possible architectural choices.
Option A: TTS with business logic
- TTS maintains a scope derivation policy engine
- Pros: Centralized scope management, guaranteed narrowing
- Cons: TTS becomes application-aware, harder to operate, doesn’t scale across different applications in the same trust domain
Option B: Caller-driven narrowing
- Services request specific scopes, TTS only validates a subset of relationship
- Pros: TTS stays generic infrastructure, scales across apps
- Cons: Requires all services to understand scope narrowing, easier to get wrong
Option C: Hybrid
- Edge services translate external OAuth scopes to internal transaction types
- Pros: TTS has a simple mapping: transaction_type → internal_scopes
- Cons: Internal services validate scopes as needed
The spec currently implies Option A without acknowledging the operational complexity. Most implementations will probably do Option B and call it good enough considering the complexities of business transactions performed by APIs.
What We Did
We punted on scope narrowing in our solution. Since Tx-Tokens spec was not in public domain, we used a service that implemented RFC 8693 Token Exchange to mint our JWTs with some proprietary claims. Our TTS did a simple scope passthrough:
- If
subject_tokenhas scopetransfer:read,transfer:create, the Txn-Token gets the same scope - Upstream services are responsible for checking if the token’s scope includes what they need
- We relied on service-level authorization: “I need
orders:read, does this token have it?”
Here Is My Take
TTS should stay as a generic infrastructure to scale across business applications of modern enterprises.
API Gateway being an infrastructure service should not deal with any non-trivial business logic, esp. when required transaction context (such as amount, limits, etc.) resides in payload. This pushes the business logic back to each service (first internal service, etc.), which has the context to know “this is a check account standing operation, it only needs account:read.”
It is understandable that the authors of the specs would not want to bloat the spec by addressing all possible design choices for TTS for the narrow scoping requirement. However, that also questions the usability of addressing the same requirement. Should it even be in the scope for the spec?
Summary
The Transaction Tokens spec is at draft-08, so it’s maturing. The core concepts such as short-lived, preservation of authorization context and immutable context tokens solve real problems.
The challenge is bridging the gap between “this should work in theory” and “this works in our production environment with 200 services, 5 datacenters, and routine network partitions.” The spec has good bones, but needs more operational battle scars to become truly production-ready.
I have a couple of other issues that I will try to discuss in subsequent posts.
Have you implemented OAuth token propagation in microservices? What problems did you hit that specs don’t cover? I’m particularly curious about TTS deployment patterns people have tried.