OAuth 2.0 Token Exchange

OAuth 2.0 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, which 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.

In the case of token exchange, however, there is more flexibility on where the security token is first issued. It can be a third-party authorization server, for example, that issues the access 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.

There are two semantically different scenarios to explore - impersonation and delegation.

Impersonation

This is the most common usage of token exchange. Consider a simple example.

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 would be introspected for validity. A JWT issued by an external party would be 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 should be validated. Verify supports several out-of-the-box subject token types 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 out-of-the-box token types 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 below.

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 would be based on the user identity asserted 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 and is issued a new user token 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 should be 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 would contain a may_act claim as below to indicate that any actor token with "clinic" set to "your_family_clinic" can request for a patient token. The payload below may be the result of introspecting a patient access token or a JWT payload, etc.

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

The may_act claim can have more than one property that must match. In addition to this, Verify offers the ability to evaluate an access policy as part of the token exchange flow to perform finer-grained authorization checks. Verify also includes the may_act claim in the SSO and token audit events.

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.

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.

Similar to the example above with the medical clinic, 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.

OpenID Connect Native SSO for Mobile Apps (Native App SSO)

Native App SSO is a specification that allows sharing of identity across multiple mobile applications. Native App SSO is not based on session cookie, it extends the OAuth 2.0 Token Exchange specification to share identity (SSO) between apps produced and signed by the same vendor.

Application 1

  • Requests a device_sso openid scope during /authorize request.
  • A device_secret is generated as a response along with tokens.

Application 2

  • The id_token and device_secret generated from Application 1 is used in token exchange request to exchange it for access and refresh token.

This is the sequence diagram of NativeApp SSO runtime flow.