Problem and context
A multi-tenant SaaS API gateway calls a billing service on behalf of a signed-in user. The identity platform issues an upstream access token for the gateway; the billing service accepts only its own audience. The gateway needs a narrower downstream token while preserving an auditable statement of who acted for whom.
Design the exchange endpoint, validation rules, claim mapping, caching, error handling, revocation, and monitoring. Assume the gateway is a controlled server-side client and browsers never hold the downstream token. The billing service must reject tokens meant for another service.
Your design should preserve five invariants:
- The exchange service accepts only validated subject tokens that are allowed to be exchanged.
- The new audience, scope, tenant, and resource reach cannot be broader than the upstream authorization.
- Subject and actor remain distinguishable and traceable; a service cannot masquerade as a user.
- A downstream service trusts only its own issuer, audience, and signing keys.
- Revocation, expiry, key rotation, and audit boundaries have measurable time limits.
What the interviewer is evaluating
Start by distinguishing token exchange, token forwarding, impersonation, and delegation. RFC 8693 defines an HTTP/JSON Security Token Service. A request can contain a subjecttoken and optionally an actortoken; the response extends a normal OAuth token-endpoint response. The protocol does not grant broader authority by itself.
The next signal is audience discipline. The gateway must not forward a bearer token addressed to itself to billing, search, and export services. Each downstream token needs one audience and only the scopes required for the call.
The third signal is the proxy relationship. Exchange is allowed only when policy says the actor may act for the subject. Claims such as mayact and act need a trusted issuer or a local policy source. Copying arbitrary client-supplied sub or tenantid into a new token is an escalation.
Finally, discuss failure modes: a stale authorization cache, a downstream that checks signatures but not audience, tokens in logs, a refresh token handed to the gateway, and an upstream revocation that remains effective until the downstream TTL expires.
Questions to clarify first
- Who is the subject and who is the actor? Here the subject is the user and the actor is the API gateway; service-account impersonation needs an explicit policy.
- Who consumes the exchanged token? Billing is the only audience, or are multiple resources allowed? Multiple audiences enlarge the blast radius.
- How is scope reduced? The upstream may have
billing:read billing:write; which one does this request need? - Where is the tenant boundary checked? Which identity, session, and resource stores are authoritative? Never trust a request-body tenant field.
- What is the revocation target? For example, stop new exchanges within five seconds and limit existing downstream tokens to two minutes.
- Does the gateway need a refresh token? A delegated call normally needs a short-lived access token; a long-lived refresh token needs a separate threat review.
A 30-second answer
“I would have the gateway call a standard token endpoint with a user subjecttoken, a gateway actortoken, a fixed billing audience, and the minimum scope. The exchange service validates issuer, signature, exp, nbf, client authentication, the subject’s delegation policy, and tenant state. A policy table maps upstream scopes to an allowed downstream scope. The new token contains only the billing audience, a short expiry, tenant context, and the subject/actor relationship; it never copies unverified claims.
The billing service trusts only its issuer, signing keys, and audience and performs resource-level authorization. Exchange decisions may be short-cached, but revocation events and a short TTL must meet the stated target. Token bodies, exchange requests, and actor secrets are redacted. Errors are generic; audit records keep request ID, subject, actor, audience, scope, and policy version.”
Step-by-step design
Step 1: Define the token and trust boundary
The exchange service is an authorization server or trusted STS, not an arbitrary gateway helper. It configures allowed upstream issuers, JWKS, client-authentication methods, downstream audiences, and policy versions. The gateway authenticates with mTLS, a private-key JWT, or another approved method; a static string alone is not proof of delegation authority.
The minimum request can look like this:
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=...
subject_token_type=urn:ietf:params:oauth:token-type:access_token
actor_token=...
actor_token_type=urn:ietf:params:oauth:token-type:access_token
audience=https://billing.internal
scope=billing:readRFC 8693 treats the requester as the client in the exchange. A resource server can temporarily act as that client to trade an incoming token for one appropriate to a backend service. The role does not itself grant new permissions.
Step 2: Validate subject and actor by source
Select a validator by token type, then check an algorithm allow-list, issuer, exp, nbf, required client, token type, and revocation state. Cache JWKS by issuer and rotate by kid; an unknown key can trigger a controlled refresh, never an algorithm downgrade.
The subject is the user or workload being represented. The actor is the gateway that actually initiated the exchange. Policy must answer whether this actor may act for this subject at this audience, not merely whether the actor is a known service. RFC 8693’s may_act can express an authorized actor; the exchanged token can express the current actor with act. Both claims need a trusted issuer or local policy.
Do not treat request-body sub, tenant_id, or roles as identity facts. Rebuild context from validated tokens and the authoritative tenant directory and reject missing or conflicting subject data.
Step 3: Compute a monotonically narrower authorization
Express authorization as a constrained intersection:
issued_scope = requested_scope
∩ subject_allowed_scope
∩ actor_allowed_scope
∩ audience_policy_scope
∩ tenant_state_scopeReject an empty or over-broad requested scope; do not silently mint a wider default. The audience comes from a service registry, not an arbitrary URL supplied by the gateway. Each audience binds allowed claims, scopes, TTL, and resource types.
Tenant state, a disabled user, billing-account ownership, and high-risk actions may require an online policy check. A token tenant_id is only part of validated context; the resource service still compares it with ownership. If a request needs several services, prefer several short-lived exchanges instead of one universal audience.
Step 4: Issue a short-lived, verifiable downstream token
The access token should carry iss, sub, aud, exp, iat, scope, tenant identity, client ID, policy version, and actor relationship. Its expiry must not exceed the upstream remaining lifetime or the business risk window. Do not return a refresh token as an ordinary exchange result.
The billing service pins issuer, audience, JWKS, and allowed algorithms and performs resource authorization on every request. Signature validation without aud validation lets one service accept a token issued for another. A sender-constrained token can further ensure that copying a bearer value is insufficient.
Step 5: Coordinate cache, revocation, and key rotation
Cache issuer metadata, JWKS, and short-lived policy results only when the key includes issuer, subject, actor, audience, scope, tenant, and policy version. Never reuse one tenant’s allow decision for another. A revocation first updates the authority and publishes an invalidation event; the exchange endpoint stops issuing within five seconds, and new downstream tokens have a maximum two-minute TTL. Billing can choose introspection or short-TTL verification by risk.
Test lost invalidation messages, node restarts, and replication lag. JWKS rotation needs bounded overlap and a traceable kid. Revoking a signing key affects every token it signed; it is not a substitute for subject-level revocation.
Step 6: Handle errors, replay, and availability
Return a uniform invalidgrant or invalidtarget-class error for unknown issuer, expiry, wrong audience, non-delegable subject, insufficient scope, and unavailable policy. Do not reveal whether a subject exists; keep a request ID and safe reason code internally.
Exchange requests contain bearer material and must not enter URLs, ordinary logs, trace attributes, or error reports. If the upstream token can be replayed, an attacker who steals gateway traffic can repeat exchanges. TLS, sender constraints, short TTLs, rate limits, and risk signals reduce the window. If policy storage is unavailable, fail closed for high-risk writes. Any low-risk read fallback needs a maximum stale-policy age.
Step 7: Audit the subject chain and tenant boundary
Record request_id, subject, actor, client, tenant, audience, requested and issued scope, token ID, policy version, issuer key ID, and decision. Never record the token body. Downstream logs reference token ID and request ID so a gateway call can be connected to a user action.
The resource service must not trust only an X-Tenant-ID header from the gateway. It reads tenant context from the signed token, then checks resource ownership, billing-account state, and approval rules. Add adversarial tests for cross-tenant access, confused deputy behavior, scope expansion, wrong audience, and replay of an old token.
Step 8: Roll out with staged evidence
Register a fixed audience and scope for one test tenant and prove that an old token cannot access billing. Canary the new issuer and key while observing exchange rejection rate, policy latency, token TTL, audience errors, cache hits, and revocation propagation. Expand to production tenants only after the measurements hold.
Keep a rollback switch that stops new issuance and restores a known-good client configuration. Do not roll back by disabling downstream audience checks or deleting audit records.
Model high-quality answer
“I treat the exchange service as a trusted STS. The gateway submits a client-authenticated user subjecttoken, its own actortoken, a fixed billing audience, and the smallest scope. The service validates every issuer, signature, time claim, token type, client, and delegation rule, then intersects requested scope with subject, actor, audience, and tenant-state policy. It rejects arbitrary audiences and any request that expands authority.
The downstream token is only for billing, expires no later than the upstream token, and records subject, actor, tenant, scope, policy version, and token ID. Billing pins its issuer, JWKS, and audience and still performs resource-level tenant authorization. Tokens and exchange requests never enter logs; audit records contain only the chain and decision metadata.
Revocation updates the authority and broadcasts invalidation, stopping new exchanges within five seconds. Downstream tokens last at most two minutes, with introspection or short-TTL validation chosen by risk. Cache keys include all identity and policy dimensions, and JWKS rotation has bounded overlap. Token exchange narrows authority and preserves accountability; it does not copy a user identity to every service.”
Common mistakes
- Forwarding the upstream bearer token. Its audience is wrong and a leak spreads laterally; exchange a narrow token per resource.
- Checking only the signature. A valid signature does not prove issuer, audience, or scope.
- Merging subject and actor. Downstream cannot tell the user from the proxy service, so audit and revocation lose meaning.
- Trusting request-body
tenant_id. An attacker can select another tenant; derive it from validated identity and resource ownership. - Assuming an actor allowed to exchange may represent anyone. Delegation needs explicit policy and a trusted source.
- Issuing a refresh token by default. The gateway needs a short-lived access token; a long-lived credential enlarges the window.
- Caching without all dimensions or with a long TTL. Authorization can cross tenants or outlive revocation; bound keys, versions, and propagation.
- Logging tokens or exchange requests. Bearer values are replayable; log token IDs, public metadata, and reason codes only.
- Using one token for every downstream. Audience and scope become too broad; exchange separately.
- Testing only success. Expiry, wrong audience, revocation, JWKS rotation, restarts, and policy failure expose the real boundary.
Follow-up questions and reference answers
What do may_act and act solve?
may_act says which actor the subject token authorizes to act; act says who currently acts for whom in the new token. Neither replaces signature, issuer, audience, or local authorization checks, and both require a trusted source.
Why not forward the user token to billing?
The upstream token usually targets the gateway and may cover several resources. Forwarding enlarges the audience and permission exposure. Exchange issues a short-lived, auditable token only for billing.
Should billing use online introspection?
It depends on revocation goals, traffic, and availability. High-risk writes can use introspection; ordinary reads can use a short-TTL JWT. Measure the worst delay; offline caching and immediate revocation cannot both be claimed without a bounded mechanism.
What if the policy service is unavailable?
Fail closed for high-risk exchanges. A low-risk read may use an explicitly bounded stale policy cache, with the fallback visible in monitoring and audit. Default allow turns an availability incident into escalation.
How do you prevent a multi-tenant cache mix-up?
Include issuer, subject, actor, tenant, audience, scope, and policy version in the cache key, and still check resource ownership. A key based only on user ID or audience can reuse one tenant’s decision for another.
The upstream token is revoked but the downstream token is still valid. What then?
Stop new exchanges immediately and let the downstream introspect or consume invalidation events according to risk. If it only verifies offline, cap TTL at the accepted maximum window and audit operations in that interval; committed operations cannot be recalled.
How do you roll back safely?
Stop new exchange, withdraw the problematic audience or policy version, retain bounded validation for the old signing key, and restore known-good client configuration. Never disable audience checks or delete audit evidence as a shortcut.