OAuth 2.0 Token Exchange: How Edge Servers Handle Token Delegation
When a user's request passes through a CDN edge server on its way to a backend microservice, the original access token cannot simply be forwarded unchanged. The edge server needs its own scoped credentials to call downstream services, yet it must preserve the user's identity for authorization decisions deeper in the stack. RFC 8693 — the OAuth 2.0 Token Exchange specification — solves this problem by defining a protocol for swapping one security token for another through a Security Token Service. For engineers building token-based authentication at the edge, understanding this exchange mechanism is the difference between a secure, auditable architecture and one that leaks privileges across service boundaries.
What Is OAuth 2.0 Token Exchange and Why Does It Matter at the Edge?
OAuth 2.0 Token Exchange, defined in RFC 8693, is a protocol extension that allows one party to exchange a security token for a different token with a different scope, audience, or token type. A Security Token Service (STS) receives the original token, validates it, applies policy, and issues a new token tailored to the requesting party's needs.
At a CDN edge, token exchange addresses a fundamental architectural tension. Edge servers sit between the client and the origin, operating as reverse proxies that terminate TLS, enforce security headers, and route requests. When these edge servers need to call backend services — for personalization data, inventory checks, or user profile lookups — they cannot use the client's original token directly. That token was issued for the client, scoped to client-facing APIs, and its audience claim restricts it to front-end services. The edge server needs a different token: one that identifies both the original user and the edge server itself, scoped to the specific backend service being called.
Impersonation vs Delegation: Two Models for Edge Token Forwarding
RFC 8693 defines two distinct models for how the exchanged token represents the relationship between the original subject and the acting party. Choosing the wrong model at the edge layer can create security blind spots that are invisible until an incident investigation reveals broken audit trails.
| Metric | Edge Processing | Origin Processing | Improvement |
|---|---|---|---|
| Latency (p50) | 8–15 ms | 120–300 ms | 15–20x faster |
| Cache Hit Ratio | 85–95% | N/A | Reduces origin load |
| TLS Handshake | 1–5 ms (nearby PoP) | 50–150 ms | 10–30x faster |
| Bandwidth Cost | $0.01–0.04/GB | $0.05–0.12/GB | 3–5x cheaper |
| Availability | 99.99% (multi-PoP) | 99.9% (single DC) | 10x fewer outages |
Impersonation
In the impersonation model, the STS issues a token where the edge server fully assumes the identity of the original user. The resulting token's sub claim contains the user's identifier and there is no act claim. From the perspective of the downstream backend service, the request appears to come directly from the user. No intermediary is visible.
Impersonation is appropriate when the edge server is a transparent proxy that adds no authorization logic of its own. Static asset delivery through a CDN caching layer, for example, where the edge simply validates the token and passes the request through. The backend applies the same authorization rules it would apply to a direct client request.
// Impersonation: exchanged token
{
"iss": "https://sts.example.com",
"sub": "user-alice-1234", // original user only
"aud": "https://api.orders.internal",
"exp": 1727740800,
"scope": "orders:read orders:write",
"iat": 1727740500
// no "act" claim - edge server is invisible
}
Delegation
In the delegation model, the STS issues a token that carries both the original user's identity and the identity of the acting party — the edge server. The sub claim remains the user, but an act claim is added containing the edge server's identity. This creates an explicit chain of custody.
Delegation is the correct model for edge computing scenarios where the edge server performs authorization decisions, transforms requests, or aggregates data from multiple backends. The act claim gives downstream services the information they need to apply different authorization policies to delegated requests versus direct requests.
// Delegation: exchanged token with act claim
{
"iss": "https://sts.example.com",
"sub": "user-alice-1234", // original user
"aud": "https://api.orders.internal",
"exp": 1727740800,
"scope": "orders:read", // narrowed scope
"act": {
"sub": "edge-us-east-1", // acting party
"iss": "https://cdn.example.com"
},
"iat": 1727740500
}
The act claim can be nested for multi-hop scenarios. When the backend service needs to call another downstream service on behalf of the same user, it performs its own token exchange, and the resulting token's act claim contains the backend's identity with the edge server's identity nested inside. This produces a complete audit trail: user Alice → edge-us-east-1 → api-orders → inventory-service.
| Aspect | Impersonation | Delegation |
|---|---|---|
Token sub | Original user | Original user |
Token act | Absent | Edge server identity |
| Backend visibility | Sees only the user | Sees user + intermediary chain |
| Audit trail | Broken at edge boundary | Complete through all hops |
| Scope narrowing | Optional | Required (principle of least privilege) |
| Use at CDN edge | Transparent proxy only | Any edge logic, personalization, aggregation |
| Zero trust compliance | Weak | Strong |
Implementing Token Exchange at the Edge: The STS Protocol
The token exchange request follows a standard OAuth 2.0 token endpoint format with a specialized grant type. Understanding each parameter is essential for configuring edge servers that participate in CDN architecture patterns involving token delegation.
The Exchange Request
The edge server sends a POST request to the STS endpoint with the following parameters:
POST /oauth/token HTTP/1.1
Host: sts.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZWRnZS11cy1lYXN0LTE6c2VjcmV0
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=eyJhbGciOiJSUzI1NiIs... (user's original JWT)
&subject_token_type=urn:ietf:params:oauth:token-type:jwt
&actor_token=eyJhbGciOiJFUzI1NiIs... (edge server's own credential)
&actor_token_type=urn:ietf:params:oauth:token-type:jwt
&audience=https://api.orders.internal
&scope=orders:read
&requested_token_type=urn:ietf:params:oauth:token-type:jwt
The subject_token is the user's original access token that arrived with the inbound request. The actor_token is the edge server's own credential — typically a short-lived JWT issued during the edge node's bootstrap process and rotated every few minutes. The audience parameter restricts the new token to the specific backend service the edge needs to call, enforcing the principle of least privilege.
Edge-Side Validation Before Exchange
Before sending the exchange request, the edge server must validate the incoming subject token locally. This validation prevents the edge from forwarding expired, revoked, or malformed tokens to the STS, which would waste network round-trips and potentially trigger rate limiting on the STS endpoint.
# Nginx configuration for edge token validation
# using nginx-jwt-module or OpenResty
location /api/ {
# Step 1: Validate incoming JWT locally
auth_jwt "edge-auth";
auth_jwt_key_file /etc/nginx/jwks/cached-keys.json;
auth_jwt_claim_set $jwt_sub sub;
auth_jwt_claim_set $jwt_aud aud;
auth_jwt_claim_set $jwt_scope scope;
# Step 2: Reject if audience doesn't match this edge
if ($jwt_aud !~ "https://edge\.example\.com") {
return 403;
}
# Step 3: Forward to token exchange subrequest
auth_request /internal/token-exchange;
auth_request_set $exchanged_token $upstream_http_x_exchanged_token;
# Step 4: Proxy to backend with exchanged token
proxy_set_header Authorization "Bearer $exchanged_token";
proxy_pass https://backend-pool;
}
The JWKS (JSON Web Key Set) used for local validation must be cached at the edge and refreshed periodically. A typical configuration fetches the JWKS from the identity provider every 5 to 15 minutes, or uses the Cache-Control headers from the JWKS endpoint to determine the refresh interval. This approach allows the edge to validate tokens without contacting the identity provider on every request, keeping validation latency under 1 millisecond.
Caching Exchanged Tokens at the Edge
The STS response includes a new token and its expiration time. Edge servers should cache these exchanged tokens to avoid repeating the exchange for every request from the same user to the same backend. The cache key combines the subject token's jti (JWT ID), the target audience, and the requested scope.
cache_key = sha256(
subject_token.jti +
requested_audience +
requested_scope
)
ttl = min(
exchanged_token.exp - now(),
subject_token.exp - now(),
max_cache_duration // typically 60 seconds
)
The TTL must be the minimum of the exchanged token's expiration, the subject token's expiration, and a configurable maximum. If the subject token expires before the cached exchanged token, the cached token becomes useless because subsequent requests will carry a different subject token. A maximum cache duration of 30 to 60 seconds provides a reasonable balance between performance and security.
Cross-Domain Federation: Token Exchange Across Trust Boundaries
In multi-cloud and multi-CDN architectures, tokens often need to cross trust boundaries between different identity providers. A request might arrive at an edge server with a token issued by the customer's identity provider (IdP A), but the edge needs to call a backend service that trusts a different provider (IdP B). Token exchange provides the mechanism for this cross-domain federation without requiring the backend to trust IdP A directly.
The edge server's STS acts as a federation bridge. It validates the incoming token against IdP A's keys, applies the customer's authorization policies, and issues a new token signed by IdP B's keys (or its own keys that IdP B trusts). This pattern appears frequently in SaaS platforms that use a CDN to front multiple customer tenants, each with their own identity infrastructure.
Configuration for Cross-Domain Exchange
# STS federation policy (pseudocode)
federation_rules:
- source_issuer: "https://customer-a.auth0.com/"
target_issuer: "https://internal.sts.example.com"
allowed_audiences:
- "https://api.orders.internal"
- "https://api.inventory.internal"
scope_mapping:
"customer:read": "orders:read"
"customer:write": "orders:read orders:write"
max_token_lifetime: 300 # 5 minutes
require_mfa_claim: true
The federation policy maps between external and internal scopes, restricts which backend audiences the external token can reach, and can enforce additional requirements like mTLS binding or MFA claims. Scope mapping is particularly important: external tokens often use customer-facing scope names that don't match the internal microservice scopes, and the STS performs the translation.
Security Considerations for Edge Token Exchange
Deploying token exchange at the edge introduces attack surface that doesn't exist in monolithic architectures. Each edge node becomes a token-issuing participant in the security infrastructure, and compromising a single edge node could grant an attacker the ability to mint tokens for any user.
Edge Node Identity and Attestation
Each edge node must have a strong, verifiable identity. Hardware security modules (HSMs) or trusted platform modules (TPMs) provide hardware-backed attestation that the edge node is running approved software on approved hardware. The edge node's actor token should be bound to its hardware identity through certificate-based authentication. When an edge node boots, it performs a measured boot process and presents its attestation report to the STS to receive its initial actor token.
Scope Reduction at the Edge
The exchanged token should always have equal or narrower scope than the original subject token. An edge server handling a read-only API request should exchange for a token with only read scope, even if the user's original token includes write permissions. This principle of least privilege limits the blast radius if an edge node or backend is compromised.
Token Binding with Confirmation Claims
The cnf (confirmation) claim binds a token to a specific cryptographic key, preventing token theft and replay. At the edge, this means binding the exchanged token to the edge node's TLS client certificate. The backend verifies that the token's cnf claim matches the certificate presented in the mTLS connection.
// Exchanged token with cnf binding
{
"sub": "user-alice-1234",
"act": {"sub": "edge-us-east-1"},
"aud": "https://api.orders.internal",
"cnf": {
"x5t#S256": "dGhpcyBpcyBhIFNIQS0yNTYgdGh1bWJwcmludA"
// SHA-256 thumbprint of edge node's TLS cert
}
}
Monitoring and Anomaly Detection
Every token exchange should be logged with the subject identity, actor identity, requested audience, requested scope, and whether the exchange was a cache hit or a fresh STS call. Monitoring systems should alert on anomalies: an edge node exchanging tokens at an unusually high rate, an edge node requesting scopes it hasn't historically needed, or exchanges for audiences that don't match the edge node's configured backend routes.
Practical Architecture: Token Exchange in a Multi-Region CDN
A production deployment of token exchange at scale requires careful consideration of STS placement, failover, and latency. Running a single centralized STS creates a bottleneck and a single point of failure. Running an STS instance at every edge PoP adds operational complexity but minimizes exchange latency.
The recommended pattern is regional STS deployment: one STS cluster per geographic region (e.g., us-east, eu-west, ap-southeast), with edge nodes in each region configured to exchange tokens with their regional STS. Each regional STS shares the same signing keys and policy configuration, distributed through a secure configuration management system. If a regional STS becomes unavailable, edge nodes can fall back to their secondary region's STS, accepting slightly higher latency over total failure.
Edge Worker Implementation
For CDNs that support edge functions, the token exchange logic can run as an edge worker rather than in the nginx configuration. This approach provides more flexibility for complex exchange policies:
// Edge worker: token exchange handler (pseudocode)
async function handleRequest(request) {
const incomingToken = request.headers.get("Authorization")?.split("Bearer ")[1];
if (!incomingToken) return new Response("Unauthorized", { status: 401 });
// Validate locally using cached JWKS
const claims = await validateJWT(incomingToken, cachedJWKS);
if (!claims) return new Response("Invalid token", { status: 401 });
// Check token exchange cache
const cacheKey = sha256(claims.jti + targetAudience + requestedScope);
let exchangedToken = await edgeKV.get(cacheKey);
if (!exchangedToken) {
// Cache miss: call STS
const stsResponse = await fetch(STS_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
subject_token: incomingToken,
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
actor_token: EDGE_NODE_TOKEN,
actor_token_type: "urn:ietf:params:oauth:token-type:jwt",
audience: targetAudience,
scope: requestedScope
})
});
const result = await stsResponse.json();
exchangedToken = result.access_token;
// Cache with appropriate TTL
const ttl = Math.min(result.expires_in, claims.exp - now(), 60);
await edgeKV.put(cacheKey, exchangedToken, { ttl });
}
// Forward to origin with exchanged token
const modifiedRequest = new Request(request);
modifiedRequest.headers.set("Authorization", `Bearer ${exchangedToken}`);
return fetch(modifiedRequest);
}
This edge worker pattern integrates with serverless CDN architectures and provides clear separation between token validation, exchange, and request forwarding. The edge KV store handles caching without external dependencies, and the fallback to STS on cache miss is handled transparently.
Performance Impact and Optimization
Token exchange adds latency to every request that requires backend calls. On a cache hit (exchanged token already cached), the overhead is under 1 millisecond — just the local JWT validation. On a cache miss, the STS round-trip adds 3 to 15 milliseconds depending on the distance between the edge node and the regional STS. For comparison, the total request path through a CDN edge to a backend service typically takes 20 to 100 milliseconds, so token exchange represents 5 to 15 percent of the total latency on cache miss.
Optimization strategies include aggressive caching of exchanged tokens (bounded by security requirements), connection pooling to the STS endpoint, and pre-warming the exchange cache for known high-traffic user sessions. API acceleration techniques like HTTP/2 multiplexing between the edge and STS reduce the per-exchange overhead when multiple exchanges happen in parallel.
Token exchange at the CDN edge is not a convenience — it is a security requirement for any architecture where edge servers interact with backend services on behalf of users. RFC 8693 provides the protocol; delegation with act claims provides the audit trail; caching provides the performance. Engineers who treat edge authentication as a simple header-forwarding problem will discover the hard way that origin protection requires every intermediary in the request path to have its own verifiable identity.