Token exchange

Security tokens, such as JSON Web Tokens (JWT), OAuth access tokens and others, facilitate the sharing of identities, authorize access to APIs, etc. across security domains and systems. A Security Token Service (STS) is a system that is able to validate security tokens provided to it and exchange it for new security tokens in response. This enables clients to obtain tailored and appropriate security tokens to facilitate access to resources and to perform operations.

OAuth 2.0 Token Exchange is an extension to OAuth 2.0 to allow an authorization server to act as a STS. This mechanism allows trusted services or clients to obtain and exchange tokens securely. Token exchange is particularly useful when a service or application needs to acquire a different type of token or a token with specific privileges to access to resource or perform an action.

Traditional OAuth flows vs token exchange

In the typical OAuth flow, the client application requests access to protected resources using an access token issued by Verify. This application is registered on Verify and requests tokens only from Verify. The user or API client authenticates with or through Verify.

However, with token exchange, there is more flexibility on where the security token is first issued. For example, it can be a third-party identity provider that issues an OIDC ID token on user authentication. The app then exchanges this token for an access token issued by Verify and then uses it to access protected resources. There are other scenarios where the initial token may also be issued by the same Verify tenant, such as to exchange a token issued to client A for a token that is used by client B.

Semantically, there are two different scenarios to explore - impersonation and delegation.

Impersonation

Impersonation is the most common usage of token exchange. Consider the following example that illustrates this.

The client application is issued an access token after the user authenticates and this token is used to access protected resource "A". "A" needs access to another protected resource "B" but the token issued to the client cannot be used for this purpose, given it lacks permissions to directly access "B". In this case, the service "A" is able to exchange the client app token for a new token that has permissions to access "B".

Here, the service "A" impersonates the subject of the token and acts on the behalf of the subject.

The basic premise here is:

  1. The input token (also called the subject token) is issued to the client for it to act on behalf of the subject or contain user identity information that identifies the subject.
  2. The output token is issued to the client to act on behalf of the subject or contain user identity information.

Thus, the input and output tokens are both linked to the same subject.

This mode of usage introduces new parameters to be included in the OAuth 2.0 token API call:

  • subject_token is the security token that is validated by Verify and is associated with the subject.

    📘

    Note

    The validation required is different for different types of tokens. For example, a token issued by the same Verify tenant is introspected for validity. A JWT issued by an external party is validated using available public keys (in the form of jwks_uri configured for the token type or in the tenant certificate store).

  • subject_token_type indicates the type of the subject token and is used to determine how it is validated. Verify supports several subject token types natively and also allows custom token types to be created. This value is usually in URN format.

  • requested_token_type is the security token type requested. An app may be able to request for different types of tokens for different purposes. Verify supports several token types natively and also has the ability to issue custom tokens, such as JWT.

Here are some common use cases for impersonation.

Access a protected resource after authenticating with an external identity provider

Here, the client application needs to access a protected resource API. In a typical OAuth flow, the app initiates an authorization code grant flow and on user authentication, the app is issued an access token. This access token can then be used to access protected resources.

However, consider a case where the user authenticates through an external identity provider and Verify does not issue the token(s). Consider further that the app is issued an identity token in the form of a signed JWT as part of user authentication. In this scenario, the app has to exchange the identity token for an appropriate access token that can then be used to access the protected resources. OAuth 2.0 Token Exchange helps here. This is illustrated as follows.

638

Key takeaways:

  • subject_token is the external identity provider issued JSON Web Token that contains the user identifier.
  • subject_token_type is a custom JWT type that is configured on the authorization server. It includes properties, such as the key material that can be used to validate the token (either as jwks, jwks_uri or some other method), any mapping configuration to identify the user on the authorization server, etc.
  • requested_token_type is the authorization server issued access token type.

📘

Note

There may be a small variant to this flow. The application generates the signed JWT instead of it being issued by the identity provider. This is used to assert the user identity to the application during authentication.

This may be relevant if the third-party provider is not able to issue a JWT or if the user can authenticate with different identity providers directly to the application. In the latter case, while Verify supports multiple custom token types, it may be useful to generate a normalized JWT to reduce the data shared with Verify in the token API call.

Resource-to-resource communication

This unpacks the example used earlier to explain impersonation.

Here, resource service A needs to make a call to another resource service B on behalf of the user. The token used by the app to authorize the request to resource service A cannot be re-used to call resource service B for the following reasons:

  1. Missing permissions to call the API on resource service B: If the app doesn't directly need to call resource API B, the token shouldn't have those entitlements.
  2. Token is sender-constrained and so, resource service A cannot use it to call resource service B.

In this case, the resource service A initiates the token exchange flow supplying the user access token in the request. A new user token is issued that can then be used to call resource API B.

628

Key takeaways:

  • subject_token is the Verify issued access token that can be validated natively.
  • subject_token_type is the native access token type supported by Verify (urn:ietf:params:oauth:token-type:access_token).
  • requested_token_type is the native access token type supported by Verify (urn:ietf:params:oauth:token-type:access_token).

Delegation

Consider a scenario where a user A needs to act on behalf of another user B. In this case, the client app needs a token that lets it act on behalf of user B but the user interacting with the app is user A. Thus, user A is the "actor" and user B is the "subject". This is very common for relationship-based authorization. For example, a family doctor who needs access to patient medical records.

While this can be achieved through impersonation (and is in fact done in that manner using older protocols like WS-Trust), this loses auditability and a prescribed method of enforcement.

There are two key differences in this mode:

  1. actor_token is included as a parameter along with a corresponding actor_token_type. This is used to validate the actor.
  2. may_act is a claim added to the subject_token that is a JSON object of one or more properties that correlate with the properties in the actor token.

For example, consider a medical clinic named "Your family clinic" and any doctor in this clinic is able to access clinic patient information. The actor_token that represents a doctor may be a JWT with the following payload.

{
    "iss": "https://jke.com",
    "sub": "docA",
    "clinic": "your_family_clinic"
}

The subject_token is a JWT that identifies the patient. The may_act claim in this token indicates that any actor token with "clinic" set to "your_family_clinic" can request for a new patient token. This token can then be used to access patient medical records. The payload that follows illustrates the subject token.

{
    "sub": "patientB",
    "may_act": {
        "clinic": "your_family_clinic"
    }
}

This mode of usage introduces new parameters to be included in the OAuth 2.0 token API call:

  • actor_token is the security token that is validated to confirm the actor's identity and their rights to request this token exchange.
  • actor_token_type is the security token type. This is similar to the subject_token_type and can be a custom type.

📘

Note

First, the subject token can be an access token issued for the patient. The goal is to identify the subject (patient) and the allowed actors (indicated by may_act). With access tokens, this information is obtained from the OAuth introspection response.

Second, the may_act claim can have more than one property and all of them must match the properties in the actor_token. Verify includes the may_act claim in the SSO and token audit events.

Third, Verify offers the ability to evaluate an access policy as part of the token exchange flow to perform finer-grained authorization checks.

Here is another common use case to illustrate this mode.

Power-of-attorney

Consider the case where an aging parent grants power-of-attorney (PoA) privileges to a child. With this, the child is able to access government services for their parent, perform bank transactions on their behalf, etc.

In this case, Verify determines if a parent token should be issued based on the relationship between the parent and child and whether the parent has consented to allowing the child to perform actions on their behalf.

648

The wrap

OAuth 2.0 Token Exchange provides the means for applications and services to exchange tokens issued by Verify or third-parties for tokens issued by Verify with configurable scopes and constraints. This can be configured in the form of standalone STS clients and within OpenID Connect applications as a grant type. Verify supports custom JWT token types in addition to the native token types, such as access tokens, refresh tokens and ID tokens. Clients and applications can be configured in Verify to use stronger forms of client authentication, such as using JWT assertions, and produce sender-constrained tokens, such as using DPoP.

💎

Vivek Shankar, IBM Security