On-Behalf-Of Flow

On-Behalf-Of Flow

develop

When using OAuth and OpenID Connect to control authentication and authorization of your users you are implementing a robust and established standards which help keep your project secure. With any of the flows offered by OAuth and OIDC you are able to get tokens which allow a given application access to a limited set of a concrete user’s resources. These limitations are manifested as claims of your 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, etc. The limited scope of those tokens gives you assurance that no unauthorized party would access resources they shouldn’t.

On-Behalf-Of (OBO) Flow

There are situations though, in which you must go past those limitations of access and ID tokens. 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 other clients or users. In general, such flow occurs when either the subject or the target identified by the original credentials is different from the actual subject or target.

Impersonation and Delegation

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

Impersonation occurs when the final credentials are indistinguishable from the original ones. The receiver of the credentials will usually be unaware of the fact that an impersonation was carried out. Some solutions can add information to the credentials informing the receiver about the impersonation, but this is in no mean required.

In case of a delegation the credentials contain explicit information that one entity delegates its rights to another entity.

Both these approaches are equally valid for performing actions on behalf of another entity. 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.

Possible Use Cases

One of the most recognizable scenarios of an On-Behalf-Of flow 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. It may also be a situation where one user is an owner of different accounts in a system. The user is able to perform action on behalf of any of their accounts by logging to just one of them. Another scenario might be a situation where a user has sub-accounts with limited scope. For example, employees of a company account have their own sub-accounts to be able to perform a limited set of actions on behalf of the company account.

A different use case is an On-Behalf-Of flow used between applications. These can be situations where one client receives an access token but needs to access another backend service using the user’s credentials. Normally an access token is meant only for one concrete receiver. In a JWT it’s manifested in the audience claim (aud). When the service wants to access another audience, it needs separate credentials in order to act on behalf of the user.

Another quite common scenario is an API Gateway exchanging an externally used opaque access token for a JWT access token passed to internal services. At first, it might seem like a typical use case of an On-Behalf-Of flow - the API Gateway wants to act on behalf of the user when calling internal services. However, this concrete use case do not need any of the solutions described in this article, as this problem can be easily solved by adopting the

Phantom Token Pattern or the Split Token Pattern.

Possible Solutions

In January 2020, RFC 8693 was published documenting the Token Exchange feature for OAuth and OIDC. The RFC describes how to exchange access and ID tokens to provide impersonation and delegation functionality. As the RFC is fairly new, it has not yet been widely adopted at the time of writing this article. This limits its usability to implement the On-Behalf-Of feature in products. The specification has also limitations for scenarios where an admin would impersonate a user, as you always need the token of the subject you want to impersonate, and it might not always be the case.

Implementing the Token Exchange specification might not always be enough, thus other solutions must be sought. Concrete solutions will be different depending on your Authorization Server vendor. Below you can find hints on how to leverage the Curity Identity Server’s features to implement delegation or impersonation scenarios.

Token Procedure

One simple solution would be to create a Token Procedure, either for the token endpoint or the authorization endpoint. The procedure can accept a request parameter with the subject who 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.

You will find an example of such implementation in the Implementing in the Curity Identity Server section below. If you want to learn more about writing Token Procedures, have a look at the documentation.

Authentication Action

Another way of achieving the goal is to use the Authentication Actions feature of the Curity Identity Server. Although such an action is not available out-of-the box, it can be easily created, either as a script action or as an SDK plugin. The action could make sure it is available only to appropriate users. If it’s implemented as a plugin it can call external services in order to perform such a check. A plugin can also present a list of subjects that can be impersonated by the current user. Finally, the 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 will need you to additionally configure a Token Procedure which will be able to read the additional property and issue tokens accordingly.

In the Implementing in the Curity Identity Server section below there is an example implementing a script Authentication Action.

Exchanging Tokens with Token Assertion

If you need to implement something closer to a typical Token Exchange you can utilise the Token Assertion endpoint and procedure. The Token Assertion endpoint is capable of accepting one JWT and issuing another one based on the received token. The procedure can read claims from the incoming token to check whether it can issue an impersonated token and what should be the new subject, then issue the token.

Implementing in the Curity Identity Server

Token Procedure on the Token Endpoint

Here is a simple example of creating a procedure in the Curity Identity Server that will be capable of creating On-Behalf-Of tokens. To create this procedure, follow these steps:

  1. In the admin UI, navigate to Profiles -> Token Profile -> Endpoints, then expand the endpoint with oauth-token type.
  2. Add a new procedure to the Authorization Code flow, by opening the procedures dropdown and selecting + New Procedure. Name the procedure appropriately. If you want to be able to use the impersonation feature in other flows, you will have to create separate procedures for them.
  3. A pop-up with the procedure code will open. Add the following parts to the default procedure:
  • At the beginning of the procedure read the act_as parameter from the request. The value of this parameter should be the subject which will be impersonated.
var actAs = context.request.getParameter('act_as');
  • Anywhere before issuing the access token add code which will change the subject of the token:
if (actAs !== null) {
    if (subjectAllowedToImpersonate(accessTokenData.subject, actAs)) {
            accessTokenData.subject = actAs
        }
    }

The subjectAllowedToImpersonate should be a method which checks whether the current subject can impersonate the subject in question. It can be a simple check whether the subject is on a list, or maybe a more complicated operation which takes into account other parameters from the authorization object. For example:

function subjectAllowedToImpersonate(subject, actAs) {
    if (subject === "admin" && actAs !== "owner") {
        return true;
    }

    return false;
}
  1. Remember to save the procedure and Commit changes to the configuration.

To test the flow, perform a standard authorization request (e.g. using OAuth.tools). Then, in the request to the token endpoint, add an act_as parameter with the value of a subject you want to impersonate. When you introspect the received token, in the sub claim you should see the value passed in the act_as parameter. Note that the ID token has the original subject. If you need the ID token changed as well add code similar to the one above, but in the part of the procedure which issues an ID token.

Transformation Procedure as an Authentication Action

Another example implementation of the On-Behalf-Of flow will use Authentication Actions.

  1. In the admin UI go to Profiles -> Authentication -> Authenticators and select the authenticator which will be capable of creating impersonated tokens. For example, you can enable this feature only for authenticators used by admin users or authenticators which require two-factor authentication, etc.
  2. In the login flow add a new action. Choose a suitable name and select the Script type action. Click Next.
  3. Click the New button next to the Transformation Procedure dropdown. Enter a suitable name for the procedure and click Create.
  4. In the script popup add the following code to the procedure:
var claims = JSON.parse(transformationContext.originalQueryParameters.get('claims'));

if (claims.id_token !== null && claims.id_token.act_as !== null) {
    var actAs = claims.id_token.act_as.value;
    if (subjectAllowedToImpersonate(attributes.subject, actAs)) {
        attributes.subject = actAs;
    }
}

The example above assumes that the subject which should be impersonated is passed in a standard claims request parameter. The parameter value is a JSON object. To match the script above use this value for the claims parameter:

{
  "id_token": {
    "act_as": {
      "value": "impersonated-user"
    }
  }
}

And as a query parameter:

claims={%22id_token%22:{%22act_as%22:{%22value%22:%22impersonated-user%22}}}

The subjectAllowedToImpersonate should be a method which checks whether the current subject can impersonate the subject in question. It can be a simple check whether the subject is on a list, or maybe a more complicated operation which takes into account other parameters from the authorization object. For example:

function subjectAllowedToImpersonate(subject, actAs) {
    if (subject === "admin" && actAs !== "owner") {
        return true;
    }

    return false;
}
  1. Remember to save the procedure and Commit changes to the configuration.

To test the flow, perform a standard authorization request (e.g. using OAuth.tools), and add the claims parameter with the value presented earlier. When you introspect the received token, in the sub claim you should see the value passed as the value for the act_as claim. Note that the ID token is also impersonated. If you need the ID token to remain unchanged you would need to use the Token Procedure instead.

Conclusion

The On-Behalf-Of 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.

Let’s Stay in Touch!

Get the latest on identity management, API Security and authentication straight to your inbox.

Keep up with our latest articles and how-tos using RSS feeds