Authorization Basics

Token Exchange

Learn what OAuth 2.0 token exchange is, how it works, and how you can use it to exchange access tokens from different sources to access tokens provided by Authorization Control Plane.

Token Exchange in a Nutshell

OAuth 2.0 Token Exchange is an extension to the standard OAuth 2.0 protocol. It enables client applications to request and obtain security tokens (such as access tokens) from an authorization server (in this case, Cloudentity) acting as a Security Token Service (STS).

STS is a service responsible for validating tokens provided to it and issuing new tokens in the response, which enables client applications to obtain appropriate security tokens for resources that are within distributed environments. In other words, a client application may request access to resources with an access token from a third-party authorization server, exchange its token to an access token provided by Cloudentity, and then use this token to authenticate the request.

Security Tokens obtained from the STS (Cloudentity) can allow the bearer to either impersonate or act on behalf of the original service. Both flows are explained below.

Token Exchange in Cloudentity

At the moment, client applications can only exchange access tokens. Exchanging refresh or ID tokens is not supported.

The verification for third-party tokens is done using an OpenID-Connect-based Identity Provider that is configured for a given workspace (authorization server). Currently, the token exchange grant flow is available for Okta, Auth0, and Generic OIDC Connector. Cloudentity does not support token exchange grant flow for SAML-based IDPs.

Cloudentity’s authorizers are also capable of exchanging third party access tokens to Cloudentity tokens when enforcing access control. To learn more, see the Using third-party access tokens for access control article.

Traditional OAuth Flow vs. Token Exchange

In the traditional OAuth 2.0 setup of Cloudentity, a client application requests access to protected resources using an access token provided by Cloudentity. This client application is registered within Cloudentity and requests tokens only from Cloudentity.

When token exchange is used, the access token source can be different as long as it comes from a trusted third-party OAuth authorization server. There are, in fact, two client applications and two OAuth authorization servers - Cloudentity and a third-party authorization server. Client application A is registered within the third-party authorization server. Client application B is registered within Cloudentity. Once Client A gets a token from the third-party authorization server, it can send a request to the Cloudentity’s OAuth token endpoint using client credentials from the Client B and passing the original token it got from the third-party authorization server. Cloudentity validates the request and mints a token that is used to authenticate the client to access protected resources. In this setup, Client A impersonates Client B, since the resource server won’t be able to distinguish between Client A and Client B when A makes a request to the resource server.

However, when exchanging the token, Client A can include an actor token. This token represents the identity of Client A. As a result, the token issued by the STS (Cloudentity) includes the act claim representing the identity of the actor:

"act": {
    "sub": "Client A"
}

This is the key difference. Now, when Client A makes a request using the exchanged token, its identity is known to the resource server. From the resource server’s perspective, A acts on behalf of B (or B delegates its privileges to A).

Learn more

To learn more about the impersonation concept in OAuth Token Exchange grant type, see the Delegation vs. Impersonation Semantics section of the OAuth 2.0 Token Exchange specification.

When To Use

Token exchange grant can be used for a variety of reasons, but there are two main and most common use cases it solves:

  • Migration enablement - it is much easier for you to migrate from a third-party OAuth or OpenID Connect provider to Cloudentity. You can make sure that your migration is going to be successful and the services protected by Cloudentity’s authorizers can consume access tokens provided by your legacy OAuth/OIDC provider.

  • Partnership enablement - if you are a Cloudentity partner that provides OAuth/OIDC authorization server capabilities, you are able to consume tokens coming from your authorization server and enforce authorization at the API request layer. Additionally, you can take a hybrid approach, where simple client applications that do not need highest level of security are registered within your authorization server, but the client applications that do need the highest level of security or need to be, for example, FAPI or Open Banking compliant, are registered within Cloudentity.

Exchanging Tokens in Depth

[mermaid-begin]
sequenceDiagram autonumber participant app as Client Application participant Cloudentity participant IDP as 3rd Party OAuth Server app->>IDP: OAuth authorization IDP->>app: 3rd Party Access Token app->>Cloudentity: request to /token endpoint Note right of app: The request includes the 3rd party access token in the payload Cloudentity->>Cloudentity: Validate the token Note Right of Cloudentity: Cloudentity checks if the token comes from a trusted IDP alt Request valid opt 3rd party token is opaque Cloudentity-->IDP: /userinfo end opt 3rd party token is JWT but with /userinfo marked in IDP's settings Cloudentity-->IDP: /userinfo end Cloudentity->>Cloudentity: Map token claims Cloudentity-->>app: Cloudentity's access token else request invalid Cloudentity-->>app: Invalid token request end
  1. A client application performs OAuth authorization to a third-party OAuth authorization server, also referred to as an Identity Provider.

  2. The server returns a third party access token.

  3. The client application makes a request to Cloudentity’s OAuth token endpoint.

    For the diagram simplification, you can see that this is one client application requesting tokens both to the 3rd party authorization server and to the Cloudentity. In fact, one client application cannot be registered within two different authorization servers to get access tokens. When the application requests a token from the 3rd party provider, it uses client credentials coming from the application registered within the 3rd party authorization server. When it requests token exchange to Cloudentity, it uses client credentials from an application that is registered within Cloudentity.

    Delegation or Impersonation

    At this stage, it is decided whether the client application will impersonate another application, or act on its behalf. If the actor_token is passed with the request, the identity of this application will be known, otherwise the application will impersonate another entity.

  4. Cloudentity validates the token.

    Trust Validation

    Cloudentity checks if the 3rd party access token comes from an Identity Provider that is configured as trusted in Cloudentity.

    For JWT tokens, the token contains the iss claim so it is easier to validate the token. Cloudentity uses the JSON Web Key Set (JWKS) of the 3rd party authorization server to validate the token signature.

    For opaque tokens, it is not possible to verify the token without calling the /userinfo endpoint of the 3rd party authorization server.

  5. If the 3rd party token is an opaque token, Cloudentity calls the 3rd party provider’s /userinfo endpoint.

    Cloudentity calls every IDP that has the token exchange grant enabled in Cloudentity’s Identities configuration to check if the 3rd party token is still valid.

  6. If your IDP has the /userinfo endpoint marked in the Cloudentity Identities configuration and the 3rd party token is a JSON Web Token, Cloudentity calls the configured /userinfo endpoint.

  7. Cloudentity maps the claims from the incoming 3rd party token to claims that are configured in Cloudentity.

  8. If the validation is successful, Cloudentity returns an access token minted by Cloudentity.

    Delegation or Impersonation

    If the actor token was passed in the request, the received token contains the act claim, responsible for identifying the requester as an actor, acting on behalf of a third party. Otherwise, the requester impersonates the third party, as it is indistinguishable from it upon presenting this token.

  9. If the validation is not successful, Cloudentity returns Invalid token request in the response body.

Token Exchange Request Details

Below, you can see an example of the request to the OAuth token endpoint using the token exchange grant type.

curl -v https://test.us.authz.stage.cloudentity.io/test/default/oauth2/token -d
"grant_type=urn:ietf:params:oauth:grant-type:token-exchange&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&subject_token=$AT"

When token exchange grant is used, the request to the OAuth token endpoint must contain the grant_type request body parameter with the urn:ietf:params:oauth:grant-type:token-exchange set. Additionally, the request body must contain the following parameters:

  • client_id - client identifier of the client application registered within Cloudentity for the purpose of the token exchange.

  • client_secret - client secret of the client application registered within Cloudentity for the purpose of the token exchange.

  • subject_token - third party authorization server’s access token to be exchanged for the Cloudentity’s access token.

  • subject_token_type - indicates the type of the security token in the subject_token parameter. If not provided, Cloudentity will assume the value urn:ietf:params:oauth:token-type:access_token - since only access token exchange is supported.

  • actor_token (optional, not in example) - a security token that represents the identity of the acting party (if one is present). Typically, this will be the party that is authorized to use the requested security token and act on behalf of the subject. The presence of this parameter means that the requestor will act on behalf of the subject, but their identities will still be separated.

  • actor_token_type (required when actor_token is present) - indicates the type of the security token in the actor_token parameter. Cloudentity expects this to be an access token.

Additional Parameters

For a detailed list of all parameters used when requesting tokens, see the OAuth token endpoint reference and the OAuth 2.0 Token Exchange specification.

Contrary to the OAuth 2.0 Token Exchange specification, Cloudentity’s implementation for the token exchange grant does not support the resource request body parameter. It’s logic is covered by the scope parameter.

The scope parameter holds a list of space-delimited and case-sensitive strings that allow the client application to specify the desired scope of the requested token. Scopes are unique within different authorization servers.

If there is a client application that requests a custom-scope from the ACME service, you do not need to explicitly provide the aud (audience) parameter in your request. The ACME service is automatically added to your token audience parameter as the custom-scope is unique to the ACME service.

Token Exchange Response Details

In response, Cloudentity issues a new token, which can:

  • Be used to impersonate another entity by the requestor if no actor token was passed in the request,

  • Be used to delegate third-party privileges to the requestor if actor token was passed in the request, and thus the requestor’s identity is known.

    "act": {
        "sub": "Client A"
    }
    

Token Exchange and Extensions Limitations

When the token exchange grant type is used, some of the features that you would get with a standard OAuth authorization for client apps that request tokens from Cloudentity are not available:

  • When using the token exchange grant type, the system will not execute any Post authentication Cloudentity’s Extension script attached to any identity provider.

  • The amr (authentication method reference) can be set only through a static field in your IDP configuration. It is not possible to modify or map the amr claim using Extensions.