Skip to content
Ashish.
All posts
Diagram illustrating the OAuth2 Token Exchange flow between Frontend-Service, Authorization Server, and Backend-Service.

OAuth2 Token Exchange: Delegation and Impersonation Flows

An examination of OAuth2 token exchange mechanisms per RFC 8693, covering delegation, impersonation, and on-behalf-of flows within Keycloak.

By Ashish Srivastava

In a distributed microservices architecture, a common failure point is the "trust chain" break. Service A authenticates a user, then calls Service B, which needs to call Service C. If Service B uses a username/password flow, it leaks credentials to every intermediate service. If Service B tries to use the user's access token from Service A, Service C cannot verify that Service B has the authority to act on behalf of that user without re-validating the entire chain. This is where the token exchange mechanism defined in RFC 8693 becomes critical. It allows a client to present an existing access token to an authorization server and receive a new access token with different scopes or audiences, effectively extending trust without re-authentication.

The Delegation Problem and the RFC 8693 Solution

Standard OAuth2 flows are designed for end-user authorization. When a client wants to access a resource, it exchanges an authorization code or refreshes a token. However, in a service-to-service chain, there is no end-user interaction at every hop. Service B needs to prove to Service C that it is authorized to act on behalf of the original user, but Service B does not possess the user's password.

RFC 8693 introduces a specific grant type: urn:ietf:params:oauth:grant-type:token-exchange. This mechanism treats the existing access token not just as a credential, but as the input for a new authentication event. Instead of asking "Who are you?" (like a password flow), the authorization server asks "What are you allowed to do based on this token?"

Consider a scenario with three actors: User, Frontend-Service, and Backend-Service.

  1. User authenticates to Frontend-Service and receives Token_A (Audience: Frontend-Service).
  2. Frontend-Service needs to call Backend-Service with Token_A. Backend-Service rejects Token_A because the audience is wrong.
  3. Frontend-Service initiates a token exchange request to the Authorization Server. It sends Token_A as the subject_token.
  4. The Authorization Server validates Token_A, determines the Frontend-Service has permission to request a token for Backend-Service, and issues Token_B (Audience: Backend-Service).

The mechanism relies on the subject_token parameter. The server must validate the signature and expiration of Token_A before generating Token_B. Crucially, the scope in the new token can be a subset of the original, or expanded if the server permits, while the audience is explicitly targeted. This prevents the "pass-the-token" attack where a stolen token is reused across services it wasn't intended for, provided the audience validation is strict.

Technical architecture diagram showing a horizontal flow : User authenticates to Frontend-Service, Frontend-Service sends subject_token to Authorization Server, Authorization Server validates and issues new token to Backend-Service. Style : clean vector, blue and green color p…

Mechanism Deep Dive: Fields and Validation

The core of the exchange lies in the HTTP POST request to the token endpoint. The request body contains specific parameters that dictate the transformation of the token.

POST /auth/realms/myrealm/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
 
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
subject_token_type=urn:ietf:params:oauth:token-type:access_token
audience=backend-service-api
scope=read write
client_id=frontend-service
client_secret=secret123

The subject_token is the artifact being exchanged. The subject_token_type tells the server how to interpret it (e.g., access_token, refresh_token, or saml_token). In the Keycloak implementation, the server extracts the claims from subject_token to determine the sub (subject) and the scope granted to the original client.

The audience parameter is the most critical security control here. Without it, a malicious client could request a token for any service. RFC 8693 mandates that the authorization server must validate the requested audience against the permissions of the subject_token and the requesting client. If the subject_token was issued to Frontend-Service with scope read, and the request asks for audience=backend-service with scope write, the server must check if Frontend-Service has the policy to delegate write access to backend-service.

Keycloak implements this by parsing the subject_token into an internal representation, checking the scope and audience claims against the client configuration, and then constructing a new token. The new token includes a audience claim matching the requested service and a scope claim reflecting the intersection of the original scope and the requested scope. By default, the sub in the new token is derived directly from the subject_token. To request a different subject (true impersonation), the client must typically include a requested_subject parameter (if supported by the AS). The audience parameter controls the target service, not the subject identity.

Impersonation vs. On-Behalf-Of

While RFC 8693 provides the generic mechanism, Keycloak applies specific semantics to two common patterns: Delegation and Impersonation.

Delegation (On-Behalf-Of) occurs when a service acts on behalf of a user to access another service. The sub (subject) in the new token remains the original user's ID. This is the standard use case for the on-behalf-of flow. The audience changes to the target service, but the identity of the user remains intact. This allows Backend-Service to log "User X accessed Resource Y" even though the request came from Frontend-Service.

Impersonation is a more privileged operation. Standard OAuth2 Token Exchange (RFC 8693) preserves the original subject (sub) from the subject_token. Changing the sub to a different user is NOT part of the standard delegation flow but requires specific administrative features (e.g., impersonate scope or admin API) distinct from the standard urn:ietf:params:oauth:grant-type:token-exchange flow. In Keycloak, this capability is often gated behind specific client roles or scope mappers that allow the sub claim to be overridden.

The distinction matters for audit trails. In delegation, the chain of trust is User -> Frontend -> Backend. In impersonation, the chain might be Admin -> Frontend -> Backend (acting as User). The scope in the exchanged token reflects this. If the requesting client has the manage-users scope, it might be able to request a token where the sub is changed to a different user, but only if the specific impersonation policies are explicitly enabled.

Keycloak's implementation of RFC 8693 supports both. The on-behalf-of flow is essentially a specific configuration of the token exchange grant type where the subject_token is an access token representing a user, and the audience is the target service. The impersonation capability is often gated behind specific client roles or scope mappers that allow the sub claim to be overridden.

Conceptual diagram comparing Delegation vs Impersonation flows. Left side : User -> Frontend -> Backend (User ID preserved). Right side : Admin -> Frontend -> Backend (User ID changed). Style : technical flowchart, distinct colors for roles, clear arrows, white background.

Common Pitfalls

When configuring token exchange, several recurring errors can compromise security:

  1. Misconfiguring Audience Wildcards: Allowing a client to exchange tokens for any audience (e.g., audience=*) effectively grants that client access to every service in the ecosystem. Always restrict the audience parameter to specific client IDs.
  2. Confusing Audience with Subject: Operators often assume that setting the audience parameter allows them to change the user identity (sub). As established, the audience defines the target service, while the sub is derived from the subject_token unless explicit impersonation features are enabled.
  3. Failing to Restrict Scope Delegation: Without strict scope mapping, a client with limited permissions (e.g., read) could request a token with elevated permissions (e.g., admin) for a downstream service. Scope mappings must explicitly define the intersection of requested and allowed scopes.

Practical Takeaways

To secure your token exchange implementation, adopt these mental models:

  • Least Privilege by Default: Assume any token exchange request is a potential attack vector. Configure clients to request only the minimum necessary scopes and audiences.
  • Trust Boundaries are Explicit: The audience claim defines the trust boundary. Never assume a token valid for Service A is implicitly valid for Service B; always validate the audience at every hop.
  • Audit the Chain: Since the sub is preserved in standard delegation, your logs must reflect the full chain. Ensure downstream services log the sub from the received token to maintain an accurate audit trail of who actually performed the action.

Keycloak Configuration and Security Implications

To enable token exchange in Keycloak, you must configure the client to allow the token-exchange grant type and define the allowed audiences.

  1. Enable the Grant Type: In the Keycloak Admin Console, navigate to the client settings. Under the "Capabilities" or "Advanced" settings, ensure the "Token Exchange" feature is enabled. Keycloak added support for token exchange (RFC 8693 compliant) in version 15 (2021).
  2. Define Allowed Audiences: You must explicitly list the clients that this client can exchange tokens for. If Frontend-Service can only talk to Backend-Service, you restrict the audience parameter to backend-service-client-id. Allowing * or wildcards is a critical security misconfiguration.
  3. Scope Mapping: Configure the "Scope" tab to map the incoming scopes to outgoing scopes. If Frontend-Service requests read, you might allow it to pass read to Backend-Service, but block admin unless explicitly granted.

The risk is significant. If a client is compromised, an attacker can use the token exchange flow to request tokens for other services without needing the user's password. Therefore, the client_secret for the exchanging client must be stored securely, and the audience restrictions must be granular.

Furthermore, the subject_token itself must be validated. Keycloak will reject the exchange if the subject_token is expired, revoked, or issued by a different realm. The iss (issuer) claim in the subject_token must match the current realm's identity provider configuration.

In summary, RFC 8693 provides the standardized mechanism to solve the service-to-service trust problem. Keycloak implements this by parsing the subject_token, validating the audience and scope constraints, and issuing a new token that extends the trust chain. The distinction between delegation and impersonation lies in whether the sub claim is preserved or altered, a decision controlled by the client's configured permissions and the scope mappers. By strictly configuring the audience and scope parameters, organizations can secure their microservices architecture against credential leakage and unauthorized lateral movement.

Conclusion

The transition from proprietary extensions to the standardized RFC 8693 has strengthened the security model for service-to-service communication in microservices architectures. By correctly implementing token exchange in Keycloak, developers can maintain a secure trust chain without exposing credentials. The careful configuration of audiences and scopes ensures that delegation remains granular and impersonation remains strictly controlled, preventing lateral movement attacks in complex distributed systems.

FAQ

Q: Can I use token exchange to impersonate a user without their consent? A: Standard RFC 8693 token exchange preserves the original subject. Impersonating a different user requires specific administrative scopes (like impersonate) and is not part of the standard delegation flow.

Q: Does the audience parameter change the user identity? A: No. The audience parameter specifies the target service for the new token. The user identity (sub) is derived from the subject_token unless a specific requested_subject parameter is used and supported by the authorization server.

Q: What happens if I request a scope that the original token doesn't have? A: The authorization server will typically deny the request or return a token with a reduced scope that is the intersection of the original scope and the requested scope, depending on your Keycloak policy configuration.

Related posts