Impersonation Approaches

Impersonation Approaches

Tokens in OAuth and OpenID Connect give applications access to a limited set of resources owned by a specific user. These limitations are manifested as claims of the tokens. For example, in an ID token, the subject claim (sub) identifies the authenticated user, the audience claim (aud) identifies the client which is supposed to make use of the token. Claims help to restrict the capabilities of a token and ensure that only authorized parties would access restricted resources.

There are situations though, in which the restriction of access and ID tokens actually results in limitations. One such feature is when one entity acts on behalf of another. This can be either users acting on behalf of other users or clients acting on behalf of users or other clients. In general, such flow occurs when the subject of the authentication (who was identified by the original credentials) differs from the one that will perform a certain action or task.

Impersonation vs Delegation

A subject can act on behalf of another subject in two ways, either through impersonation or delegation.

Impersonation occurs when the subject in a token is indistinguishable from the original one. The process allows a subject to change to a different subject. The receiver of the token will usually be unaware of the fact that impersonation is being carried out. An application or API cannot determine by looking at the token if the subject is the entity that was actually logged in or not. Some solutions can add information (e.g. an act claim) to the credentials informing the receiver about the impersonation, but this is by no means required.

In case of a delegation a token contains explicit information that one subject delegates its rights to another entity. There may be conditions for the delegation. For example, a subject can decide to only delegate certain rights to another subject. As a result, a subject has much more control over the access rights another entity gets in its name compared to impersonation where one subject completely takes over another.

Both approaches, impersonation and delegation, are equally valid for performing actions on behalf of another subject. Whether you should choose one or the other will usually depend on the concrete use case and special requirements like the level of auditability needed, etc.

Use Cases

Impersonation Use Cases

One of the most recognizable scenarios of an impersonation or delegation is a situation where one user acts in the name of another user. For example, a system administrator takes on a user's identity to verify an issue reported by the user.

A different use case is also a situation where one user is the owner of several accounts in a system. The user is able to perform actions on behalf of their accounts by logging in to just one of them.

Another scenario might be a situation where there is a common account and a user has sub-accounts with limited scope. For example, employees of a company have their own accounts to be able to perform a limited set of actions on behalf of the company (company's account).

A different use case is where one service receives an Access Token but needs to access another backend service on behalf of the user. In this case it needs a separate token for the backend service in order to act on behalf of the user. However, this use case differs from impersonation and delegation because the subject represented by the token does not change. Checkout the article on Token Sharing for more details on calling downstream services.

A related technical scenario is an API Gateway exchanging an externally used opaque Access Token for a JWT Access Token passed to internal services. This use case looks like an impersonation flow where the API Gateway wants to act on behalf of the user when calling internal services. However, the setup does not require any of the approaches listed here. It can be easily solved by adopting the Phantom Token Pattern or the Split Token Pattern.

Protocol Standards

Token Exchange (RFC 8693)

In January 2020, RFC 8693 was published documenting the Token Exchange feature for OAuth and OpenID Connect. The RFC describes how to exchange access and ID tokens to provide impersonation and delegation functionality. As the standard is fairly new, it has not yet been widely adopted at the time of writing this article. This limits its usefulness for implementing standardized impersonation and delegation features in applications. The specification always requires the token of the impersonated subject, which is not possible in some use cases.

Utilizing OAuth and OpenID Connect

When running an impersonation or delegation flow the client needs to inform the Identity Server which subject it intends to run as. To achieve this, use a prefix scope such as act_as or act_as_account. Alternatively use the claims request parameter for specifying an identifier of the subject that the user requests to impersonate or delegate access to. In both ways the request confirms to the OAuth and OpenID standards and integration is fairly easy.

Always consider adding impersonation or delegation information to the token for audibility for example by adding corresponding claims to the default scope. A safeguard for the future when these details otherwise easily are forgotten.

Study the following abstract of claims of a token that resulted from an impersonation or delegation flow. The token represents the subject user123 but the actual actor is admin. The actor is the entity that acts on behalf of the subject and that was authenticated before the token was issued.

{
  "jti": "e26f13a2-dde6-4f94-b9c7-49230535d8bd",
  "delegationId": "6c209adb-7e3a-4608-8c99-46d8e908fc86",
  "nbf": 1646641575,
  "exp": 1646642175,
  "iss": "https://idsvr.example.com/oauth/v2/oauth-anonymous",
  "client_id": "test-web-client",
  "scope": "openid profile read write",
  "sub": "user123",
  "aud": "test-web-api",
  "iat": 1646641575,
  "purpose": "access_token",
  "act": {
    "sub": "admin"
  }
}

The following claims set shows another approach for formatting a token for delegated access. The token contains an additional claim that was called act_as that points to the subject (user123) that the user (admin) intends to act as.

{
  "jti": "1ba58403-9084-469c-ab8f-c90e36245940",
  "delegationId": "b3691c08-f57f-43da-a6ba-5d382651bcc3",
  "nbf": 1646641847,
  "exp": 1646642447,
  "iss": "https://idsvr.example.com/oauth/v2/oauth-anonymous",
  "client_id": "test-web-client",
  "scope": "openid profile read write",
  "sub": "admin",
  "aud": "test-web-api",
  "iat":1646641847,
  "purpose": "access_token",
  "act_as": {
    "sub": "user123"
  }
}

The receiving application needs to explicitly process the act_as claim for the delegation to work which adds implementation and maintenance costs. However, this approach improves security because the token represents the authenticated user and not a different subject. Consequently, this adds audibility and traceability as the subject is also the actor. In addition, scopes can have individual lifetimes allowing a setup where the scope that gives a user delegated access to another user's resources expires and where future tokens do not support delegation anymore.

Impersonation Flow

There are different approaches for implementing impersonation and delegation with the Curity Identity Server:

  1. Exchanging Tokens
  2. User Assertion Grant
  3. Customizing OAuth and OpenID Connect Flows
  4. Embedded Tokens

Exchanging Tokens is applicable when the client or service has a token that is not sufficient for subsequent requests and needs to update the existing token, for example when dealing with Token Sharing. Token Exchange or User Assertion Grant are two different flows that can be used for exchanging an existing token for a new one in a standard manner. Claims Providers, Token Procedures and scripted Authentication Actions allow customization of OAuth and OpenID Connect flows that issue tokens which contain impersonation or delegation information. A new token may embed other tokens, e.g. a delegated token that can be used in requests to downstream services.

Exchanging Tokens

The Curity Identity Server has had support for token exchange long before the flow was standardized in RFC 8693. Though the technical details differ, the purpose is the same: The client sends a request to the token endpoint to exchange a given token for a new one. The new token is normally a subset of the original token, thus contains less authorization (i.e. fewer audiences or scopes) than the original. This is important to keep in mind when using prefix scopes as the requested scope for the new token must be part of the scopes of the original token. Also, the new token will have the same claims as the original token. How the original token was retrieved is out of scope of this flow.

Exchanging a token

Adding a custom Token Procedure allows adaption of the flow such as performing checks according to the business rules before issuing a token. This approach may be useful when a user has several accounts and needs to switch between them.

User Assertion Flow

The User Assertion flow is a short name for providing a JWT assertion as an authorization grant, a mechanism that is standardized in RFC 7523. Basically, the client sends a JWT that authenticates the user in the request and receives a new token in response. In comparison to the token exchange flow the presented token can be issued by any trusted issuer other than the Curity Identity Server (federated identity) including the client itself. The presented token can even be an Access Token that the client wants to exchange for a different one.

JWT User Assertion

The new token is not limited to the same scopes or claims of the presented token which provides great flexibility. Consequently, a Claims Provider can customize the claims for the new token such as adding an act claim as defined in the Token Exchange standard.

User Assertion Grant is for example suitable in a use case where impersonation is necessary but user interaction not desired or possible. The client can prepare the JWT assertion for the new subject and retrieve an impersonated token for the same. A Token Procedure can perform checks to comply with business rules and define the details of the new token.

Customizing OAuth and OpenID Connect Flows

Instead of exchanging tokens, a client can trigger an OAuth or OpenID Connect flow to create new tokens. The claims request parameter or prefix scopes can hold the subject for the impersonation or delegation. In this way the impersonation flow still follows the standard which enables easy integration and maintenance.

Delegation with OAuth and OIDC

The user who requests impersonation authenticates with any supported authentication method. If there is an existing session with the Curity Identity Server, consider SSO for minimizing user interaction. This approach is suitable for any use case where the user interacts and authenticates with the system. Even if the user is not available for authentication or authorization, for example when running a batch job, then a Client Credentials flow with prefix scopes can solve the challenge.

Actions help to customize the flow. For example, an action may prompt the user to enter an account to act as, using a familiar user identifier such as an email. Impersonation is handled solely by the Curity Identity Server. The client keeps sending plain OAuth or OpenID Connect requests.

Impersonation with OAuth and OIDC

Embedded Tokens

If the resource server AKA API is expected to query another service and if the downstream service should receive a tailored token, then the Identity Server could issue a token that contains another token. Token Procedures can be used to highly customize a token. For example, the Token Procedure can create more than one token and embed one in the other. The embedded token can have narrower scopes and any claim values required by the external service compared to the outer token that is intended for the API. The API can easily extract the embedded token and use in its calls to the downstream service. The downstream service will be able to authorize the request but will not receive any excess privileges or data.

Embedded tokens can be used in any OAuth or OpenID Connect flow where an Access Token is issued or returned. This approach is suitable for use cases where one application calls another application on behalf of a user and where the dependency is known upfront as the token must be embedded at the time it is issued. Read more about calling external services and embedded tokens in the article about Token Sharing.

Building Blocks

Authentication Actions, Claims Providers and Token Procedures are the central building blocks for implementing an Impersonation flow with the Curity Identity Server. Custom Claims Providers ensure that the appropriate claims are populated. Token Procedures can process the claims, format and issue the tokens according to the needs of the client and APIs. Depending on the use case use one or combine any of the options for customization of the flow and tokens.

Authentication Actions

An authentication process can include Authentication Actions. They are triggered after the user authenticates but before the session is committed. It is possible to chain and orchestrate different actions and in this way create a highly adaptable authentication process.

As part of an Impersonation flow an Authentication Action, either as a script action or as an SDK plugin, could make sure the process is only available to appropriate users. A plugin can call external services in order to perform such a check.

An Authentication Action could also prompt the user to select an account for impersonation as part of the authentication. In this case, the client is not even aware that impersonation takes place. It is possible to sequence Authentication Actions.

An action can change properties of the authentication so that tokens are issued with the impersonated subject. This can be achieved by either changing the owner property on the authentication attributes or by setting an additional, proprietary attribute. The latter option requires the configuration of a Claims Provider or Token Procedure that will be able to read the additional property, and issue the claims and tokens accordingly.

Note that Authentication Actions only run when a user authentication (either login or SSO) is required. In particular, authentication is not triggered when exchanging tokens or running a Client Credentials flow.

Claims Provider

Whenever a new token gets issued, Claims Providers collect the values for the different claims that may or may not be included in a token. Which claims that end up in which token depends on the configuration.

The Claims Provider is the authority of claims and as such can access authentication subject attributes or request parameters such as a prefix-scope or claims request parameter. In an Impersonation flow the Claims Provider could process the prefix-scope from a request or the authentication attributes and create claims accordingly such as an act_as or act claim.

Token Procedure

The Token Procedure is the final instance that issues a token. Attach it either to the token endpoint or the authorization endpoint.

The procedure can process the claims or request parameters, lookup the subject that should be impersonated, then verify whether the current subject is allowed to impersonate the requested subject. Finally, it can issue tokens with the respective claims changed. It can issue several tokens at once and embed one token in another.

If you want to learn more about writing Token Procedures, have a look at Reference documentation for Token Procedures.

Example

The tutorial on how to implement an Impersonation flow demonstrates an orchestration of the building blocks as part of an Impersonation flow. It shows how to prompt the user for the subject to impersonate with the help of an Authentication Action, how to configure a claim together with a custom Claims Provider and how to issue the impersonation token in a Token Procedure.

Token Lifetime

The delegation object is the root of every token in the Curity Identity Server. It represents the user's implicit or explicit approval of the client application to do things on his or her behalf. The semantics of the delegation object change when using impersonation. In this case, the claims covered by a delegation represent a different user than the one who authenticated and approved that the client is allowed to do things on the other user's behalf. The delegation represents also the lifetime of a token. As long as there is an active delegation a client may request new Access Tokens for an authenticated user for the scopes and claims covered by the delegation. In other words, impersonation of a specific user by another user with the same client will work as long as the delegation is not revoked or expired.

Normally, a new delegation is issued every time a new token is issued. However, in the exchanging token flow it is suitable to reuse an existing delegation. Even in other use cases for impersonation the new token may be bound to an existing delegation. In this way the impersonation token has the same lifetime and lifecycle as a default token for the authenticated user.

Conclusion

The impersonation flow can prove to be very useful in many projects. Whether it is used for impersonating users to simplify issue resolution or for limiting scopes of tokens used with backend services you can use features of the Curity Identity Server to implement an appropriate solution. Should you have any comments or questions about the flow or the implementation do not hesitate to contact us.