Table of Contents

Impersonation vs. Delegation: Getting On-Behalf-Of Tokens Right

TL;DR: Impersonation and delegation both let one actor perform actions for another, but they differ in how identity is represented and audited. This article walks through how each model works in OAuth 2.0 token exchange, using request examples, JWT claims, and code snippets, then covers the implementation choices that affect scope, auditability, and privilege escalation.

Víctor Jiménez
Víctor Jiménez

Technical Writer at RootNode

Summarize:

Read
0%
Graphic comparing impersonation vs. delegation, with human and AI agent identity cards representing different ways an actor can access resources on behalf of a user.

Table of Contents

Read
0%

Delegation and impersonation are mechanisms for temporarily granting permissions to agents (either traditional automations, humans, or AI) so they can perform tasks as (or on behalf of) another user.

It’s similar to the sudo command in Unix systems. Sometimes you want to perform a task that requires privileged permissions, like installing an app. When you are using the sudo command you become (impersonate) the root user to the eyes of processes and logs, so you can run privileged tasks, and then revert to your regular user permissions when you are done.

Or, think about an AI agent accessing external resources on behalf of several users. Following AI agent security best practices, this agent should have access to resources only for the short time it takes to perform the task, and its permission level should be tied to the user requesting the task.

These are examples of impersonation and delegation, and the RFC 8693 extension to OAuth allows you to implement these flows in your applications in a standardized fashion.

In this article, we’ll:

  • Discuss the differences between impersonation and delegation.
  • Dive into the technical implementation details in OAuth.
  • Cover some tips for developers implementing impersonation and delegation in their platforms.

How Does Delegation Differ from Impersonation?

Both impersonation and delegation are mechanisms for one actor, who already has its own identity, to act as another actor.

The difference between the two is the level of transparency:

When the actor impersonates the user:

  • They identify as the user.
  • They get the same permissions as the user.
  • The resource servers aren’t aware of the impersonation.

An impersonation token looks like a regular access token, so you usually don’t need to adapt existing services to support an impersonation flow.

When the actor uses delegation on behalf of the user:

  • They keep their identity while acting on behalf of the user.
  • The other actors may be aware of the delegation chain, so they may treat it differently.

Delegation tokens also look like regular access tokens, but they contain an extra claim for the delegation chain. You can adapt your services to understand this claim, but they will work fine if you don’t; they just won’t be aware that a delegation is taking place.

So, it’s not a technical choice between impersonation and delegation. The deciding factor comes down to compliance and governance policies. Do you need the extra audit trail, and where do you need it?

Let’s dive into the technical differences now.

How Do Impersonation and Delegation Work on OAuth?

The standard way to implement impersonation and delegation in OAuth 2.0 is defined in the RFC 8693 extension. This extension covers token exchange, of which impersonation and delegation are a subset of use cases.

Under this extension, when a resource server:

  1. Receives a specific request.
  2. Acts as an OAuth client to request a new access token.
  3. To access another protected resource.

We’ll explore this impersonation and authorization flows with an example in which a frontend service needs to access a backend server to fulfill a user’s request:

Check out What Is OAuth? to refresh the basics of OAuth.

Note: Although the standard is open to different types of tokens, like OIDC JWTs or SAML assertions, the most common option for exchanges is JWTs. It’s what RFC 8693 uses in its examples, and it’s what we’ll use in this article.

Impersonation on OAuth

Step 1: The flow starts with the frontend.example.com resource server receiving a request from the user:

GET /resource HTTP/1.1
Host: frontend.example.com
Authorization: Bearer accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC

Step 2: It turns out that to fulfill this request, the frontend.example.com resource server needs to access the backend.example.com resource. So, the frontend requests a token exchange from the authorization server: as.example.com.

The token exchange request would look like this:

POST /token HTTP/1.1
Host: as.example.com
Authorization: Basic cnMwODpsb25nLXNlY3VyZS1yYW5kb20tc2VjcmV0
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&resource=https%3A%2F%2Fbackend.example.com%2Fapi
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token

The first thing we notice is that the resource server already has its own access token for the authorization server and includes it in the Authorization header.

Also, in the request body:

  • grant_type indicates it’s requesting a token exchange.
  • resource is the service’s URI that will consume the new token.
  • subject_token matches the authorization token the user sent in the initial request.
  • subject_token_type indicates that the token is an OAuth access token.

The standard considers other optional fields:

  • You can use the audience field instead of resource to provide a logical name, as in SAML or OIDC resources.
  • scope to request a specific list of OAuth scopes for the new access token.

Note: The authorization server may require additional parameters, like client_id, in certain scenarios. We kept our examples to the spec’s minimum requirements for token exchange.

Step 3: As this authorization server issued both tokens, it knows that:

  • The subject_token corresponds to the bdc@example.com user.
  • The token in the Authorization header is from the frontend.example.com service.

If both the user and the resource server have access to the new resource, the authorization server will respond with a new JWT access_token:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store

{
 "access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjllciJ9.eyJhdWQiOiJodHRwczovL2JhY2tlbmQuZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNDQxOTE3NTkzLCJpYXQiOjE0NDE5MTc1MzMsInN1YiI6ImJkY0BleGFtcGxlLmNvbSIsInNjb3BlIjoiYXBpIn0.40y3ZgQedw6rxf59WlwHDD9jryFOr0_Wh3CGozQBihNBhnXEQgU85AI9x3KmsPottVMLPIWvmDCMy5-kdXjwhw",
 "issued_token_type":"urn:ietf:params:oauth:token-type:access_token",
 "token_type":"Bearer",
 "expires_in":60
}

Step 4: This new token can be used to access the backend.example.com resource.

GET  /api HTTP/1.1
 Host: backend.example.com
 Authorization: Bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IjllciJ9.eyJhdWQiOiJodHRwczovL2JhY2tlbmQuZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNDQxOTE3NTkzLCJpYXQiOjE0NDE5MTc1MzMsInN1YiI6ImJkY0BleGFtcGxlLmNvbSIsInNjb3BlIjoiYXBpIn0.40y3ZgQedw6rxf59WlwHDD9jryFOr0_Wh3CGozQBihNBhnXEQgU85AI9x3KmsPottVMLPIWvmDCMy5-kdXjwhw

You can use jwt.io to explore the contents of the JWT’s payload:

{
  "aud": "https://backend.example.com",
  "iss": "https://as.example.com",
  "exp": 1441917593,
  "iat": 1441917533,
  "sub": "bdc@example.com",
  "scope": "api"
}

We can observe in the sub claim that the frontend server identifies itself as bdc@example.com, the user. This is impersonation, as the second resource server believes they are serving the original user and isn’t aware that there’s an intermediary.

Also note how the aud claim matches the resource parameter in the token request, which is the backend server’s URI.

Finally, the issuer (iss) of this new token is the as.example.com authorization server.

Delegation in OAuth

The flow for the delegation use case is quite similar, with two main differences.

First (step 2), the POST request includes an additional token from the resource server in the actor_token parameter:

POST /as/token.oauth2 HTTP/1.1
Host: as.example.com…
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&resource=https%3A%2F%2Fbackend.example.com%2Fapi
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token
&actor_token=cnMwODpsb25nLXNlY3VyZS1yYW5kb20tc2VjcmV0
&actor_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token

The extra actor_token parameter represents the identity of the actor that will act on behalf of the subject (the user). In this case, it contains the frontend’s access token.

When the authorization server returns the new access token (step 3), it will contain an extra act claim:

{
  "aud": "https://backend.example.com",
  "iss": "https://as.example.com",
  "exp": 1441917593,
  "iat": 1441917533,
  "sub": "bdc@example.com",
  "act": {
    "sub": "https://frontend.example.com"
  },
  "scope": "api"
}

We can observe that the sub claim remains as bdc@example.com.

However, the act claim now indicates that the bearer of this token is frontend.example.com acting on behalf of sub.

This is a delegation, as the backend.example.com resource server can check these claims to know the underlying actor and modify its behavior accordingly.

Read our article on token exchange for more advanced examples in which the subject and actor tokens are JWTs.

The Delegation Chain

The act claim can also hold an entire delegation chain. This materializes as nested sub and act claims.

{
  "aud":"https://service26.example.com",
  "iss":"https://issuer.example.com",
  "exp":1443904100,
  "nbf":1443904000,
  "sub":"user@example.com",
  "act":
  {
    "sub":"https://service16.example.com",
    "act":
    {
      "sub":"https://service77.example.com"
    }
  }
}

In this example:

  • The user initiating the request is user@example.com.
  • The service77 resource server got the first request.
  • Then, this server delegated to service16.
  • Finally, service16 can use this token to access service26 (aud claim).

The may_act Claim

The token exchange extension does not define how the authorization server should verify that the agent has permission to act on the user’s behalf.

However, it provides an optional mechanism for the user to explicitly consent to this exchange.

If the original user access token is in JWT format (step 1), it can include a may_act claim:

{
  "aud":"https://consumer.example.com",
  "iss":"https://issuer.example.com",
  "exp":1443904177,
  "nbf":1443904077,
  "sub":"user@example.com",
  "may_act":
  {
    "sub":"admin@example.com"
  }
}

In this example, user@example.com is explicitly authorizing admin@example.com to act on their behalf.

Read our article on token exchange to learn more about this spec.

Impersonation and Delegation Tips for Developers

Implementation Tips for Token Exchange

Token exchange best practices apply to both impersonation and delegation:

Use expiration dates: Although the standard does not prohibit returning long-lived tokens, it is a best practice to set the shortest expiration dates possible, adapted to the task. Exchanging a token is a lightweight operation, so you should code your clients to discard the received tokens after the task is done rather than holding onto them.

You can use IAM tools to enforce expiration dates and implement complex policies like requesting human approval or granting access based on time of day.

Use scope to limit access: When setting up your authorization server, limit the scopes of exchanged tokens to the minimum required for the task.

Implementation Tips for Delegation

We previously mentioned that supporting impersonation doesn’t require special changes on the services. However, implementing delegation can feel like playing 3D chess, with layers upon layers of identities and unexpected interactions.

Here are some tips to avoid the most common pitfalls.

Honor the act claim: Your resource server is free to base its decisions solely on the sub claim, ignoring the delegation chain. However, you should at least log the act claim for auditing purposes.

Consider also which scopes of your application shouldn’t be delegated. For example, a support human agent troubleshooting a ticket for a user, impersonating them, shouldn’t access the user’s direct messages. Restrict access to these scopes from exchanged tokens.

This best practice limits the damage a bad actor can cause if one agent is compromised.

Some authorization servers can work alongside IAM tools to implement policies like this one across all your organization.

Constrain delegation: Most impersonation use cases are straightforward. In cases like an admin user acting as a regular user, or an AI agent acting on behalf of a user, it’s usually safe for the middleman to obtain all of the user’s permissions.

However, think about a support technician solving an issue for an admin user by impersonating them. Should the technician inherit admin permissions? Probably not; it would be a security flaw enabling privilege escalation.

Identify these cases and constrain delegation so a compromised agent cannot use delegation to escalate privileges. Code could look something like:

class CombinedPermissions implements User {

  let subject;
  let actor;

  hasPermission(permission) {
    return subject.hasPermission(permission) && actor.hasPermission(permission);
  }

}

A recent example of this concept, although not related to OAuth, is Kubernetes 1.36 constrained impersonation. Without this feature, a user with permission to impersonate other users was able to impersonate a cluster administrator, resulting in a privilege escalation.

Conclusion

As adoption for automation grows, machines are acting more and more on behalf of humans. With agentic AI, automation is expanding to a whole new set of tasks. A transparent model for delegation is needed now more than ever.

Luckily, the answer has been around for a while, as OAuth 2.0’s token exchange provides this model.

With impersonation, agents have access to protected resources only when needed.

And, building on that, delegation provides transparency so that applications know when they are dealing with an agent.

Frequently Asked Questions About Impersonation and Delegation

What is the difference between impersonation and delegation?

With impersonation, an actor identifies as the user and the downstream resource may not know an intermediary is involved. With delegation, the actor retains its own identity while acting on behalf of the user, allowing the delegation relationship to remain visible.

How does OAuth support impersonation and delegation?

OAuth 2.0 supports these patterns through the RFC 8693 token exchange extension. A service can exchange an existing token for a new access token intended for another protected resource.

What is the act claim in OAuth delegation?

The act claim identifies the actor operating on behalf of the token subject. It can also represent a chain of delegation when multiple services act on behalf of one another.

Should delegated access tokens be short-lived?

Yes. The article recommends setting the shortest practical expiration period and having clients discard exchanged tokens after the task is complete rather than retaining them.

How can developers reduce the risk of privilege escalation with delegation?

Developers should limit token scopes, consider the identity of both the subject and actor, log delegation information, and constrain which permissions can be inherited through delegated access.

Related Reading

Víctor Jiménez
Víctor Jiménez

Víctor Jiménez is an engineer and technical writer who specializes in turning complex engineering concepts into clear, practical explanations. He began his career as a full-stack software engineer, while also working as a MySQL database administrator and certified instructor. After moving into technical marketing, Víctor focused increasingly on educational content for technical audiences. His recent work covers cloud infrastructure, containers and cybersecurity. Outside of work, he experiments with 3D printing, builds LEGO sets and hosts Dungeons & Dragons campaigns.

You might also like

Centralized authorization eliminates repeated user consent, but autonomous agents still need runtime policy, short-lived credentials, and audit.
The latest rankings show which risks become more pressing when a model can do more than generate an answer.
Already run CrowdStrike AIDR? See how it builds on Aembit’s identity control plane for AI agents. Read the full breakdown.