RFC 8693 OAuth 2.0 Token Exchange

Introduction

RFC 8693 OAuth 2.0 Token Exchange is a technical specification that defines a way to get a new token by presenting an existing token and optionally one more existing token at the token endpoint. The mandatory input token is called “Subject Token” and the optional input token is called “Actor Token”.

The diagram below illustrates the token exchange flow.

The specification is very flexible. To put it the other way around, the specification does not define details that are necessary for secure token exchange. Therefore, authorization server implementations have to complement the specification with their own rules.

Specification

Token Types

Thee tokens appear in the token exchange flow. They are a subject token, an actor token and a newly issued token. As possible types of the tokens, the specification lists the following token types and assigns token type identifiers to them. The identifiers of token types are registered at OAuth URI of OAuth Parameters of IANA (Internet Assigned Numbers Authority).

Token Type Token Type Identifier
JWT urn:ietf:params:oauth:token-type:jwt
Access Token urn:ietf:params:oauth:token-type:access_token
Refresh Token urn:ietf:params:oauth:token-type:refresh_token
ID Token urn:ietf:params:oauth:token-type:id_token
SAML 1.1 Assertion urn:ietf:params:oauth:token-type:saml1
SAML 2.0 Assertion urn:ietf:params:oauth:token-type:saml2

Subject Token

A subject token “represents the identity of the party on behalf of whom the request is being made.” (excerpt from Section 2.1. Request of RFC 8693)

The specification expects that the subject identified by the subject token is used as the subject of the newly issued token.

Actor Token

An actor token “represents the identity of the acting party.” (excerpt from Section 2.1. Request of RFC 8693)

If there is a need to distinguish the subject of the newly issued token from the acting party that uses the token, authorization server implementations may utilize the actor token and embed information about the acting party in the token to issue. Section 4.1 and Section 4.4 of RFC 8693 define JWT claims related to the acting party.

Token Exchange Request

A token exchange request is a kind of token requests.

Grant Type

To distinguish token exchange requests from other token requests, a new grant type urn:ietf:params:oauth:grant-type:token-exchange is defined in the specification. The value is used as the value of the grant_type request parameter of a token request.

Client Identification and Authentication

The specification does not require client authentication and even client identification at the token endpoint. Section 2.1. Request of RFC 8693 states it as follows.

The supported methods of client authentication and whether or not to allow unauthenticated or unidentified clients are deployment decisions that are at the discretion of the authorization server.

Technically speaking, “unauthenticated clients” means public clients (cf. RFC 6749 Section 2.1. Client Types) and “unidentified clients” means that a token request does not contain information whereby to identify the client making the request.

Although Appendix 1.1 of RFC 8693 shows an example of token exchange request from an unidentified client, it is risky to allow arbitrary unidentifiable clients (API callers) to make token exchange requests unconditionally. This is the reason that authorization server implementations have to complement RFC 8693 by defining their own rules for secure token exchange.

Request Parameters

Request Parameter Necessity Description
grant_type REQUIRED The value urn:ietf:params:oauth:grant-type:token-exchange indicates that the token request is a token exchange request.
resource OPTIONAL Resources to be tied to the newly issued token. This request parameter may be specified multiple times. See RFC 8707 Resource Indicators for OAuth 2.0 for details.
audience OPTIONAL Audience of the newly issued token. This request parameter also may be specified multiple times.
scope OPTIONAL A list of space-delimited names of scopes that are to be tied to the newly issued token.
requested_token_type OPTIONAL The token type of the newly issued token that the client wishes. One of the registered token type identifiers.
subject_token REQUIRED The value of the subject token.
subject_token_type REQUIRED The token type of the subject token. One of the registered token type identifiers.
actor_token OPTIONAL The value of the actor token.
actor_token_type OPTIONAL The token type of the actor token. One of the registered token type identifiers. When the actor_token request parameter is given, this request parameter is REQUIRED. On the contrary, when the actor_token request parameter is not given, this request parameter must not be present.

Token Exchange Response

A token exchange response is a kind of token responses.

Response Parameters

Response Parameter Necessity Description
access_token REQUIRED Regardless of the token type, the value of the newly issued token is set to this response parameter.
issued_token_type REQUIRED The token type of the newly issued token. One of the registered token type identifier.
token_type REQUIRED When the token type of the newly issued token is urn:ietf:params:oauth:token-type:access_token, this response parameter has the same meaning as defined in RFC 6749 The OAuth 2.0 Authorization Framework. In other cases, "N_A" is set.
expires_in OPTIONAL The lifetime of the newly issued token in seconds.
scope OPTIONAL Scopes tied to the newly issued token. When the actual set of scopes tied to the newly issued token is different from the set requested by the token exchange request, this response parameter is REQUIRED.
refresh_token OPTIONAL A refresh token (cf. RFC 6749 Section 6. Refreshing an Access Token).

Implementation

RFC 8693 is supported by Authlete 2.3 onwards.

Response from the /auth/token API

To support Token Exchange, a new action value TOKEN_EXCHANGE was added. When the value of the grant_type request parameter of a token request is urn:ietf:params:oauth:grant-type:token-exchange and the request passes basic validation steps which are performed on Authlete server side, the value of the action response parameter in the response from Authlete’s /auth/token API holds TOKEN_EXCHANGE.

Authorization server implementations have to process the TOKEN_EXCHANGE action to support Token Exchange. The switch statement below excerpted from TokenRequestHandler.java in the authlete-java-jaxrs library is an example of processing the TOKEN_EXCHANGE action. The switch statement has a case entry for TOKEN_EXCHANGE.

// Dispatch according to the action.
switch (action)
{
    case INVALID_CLIENT:
        // 401 Unauthorized
        return ResponseUtil.unauthorized(content, CHALLENGE);

    case INTERNAL_SERVER_ERROR:
        // 500 Internal Server Error
        return ResponseUtil.internalServerError(content);

    case BAD_REQUEST:
        // 400 Bad Request
        return ResponseUtil.badRequest(content);

    case PASSWORD:
        // Process the token request whose flow is "Resource Owner Password Credentials".
        return handlePassword(response);

    case OK:
        // 200 OK
        return ResponseUtil.ok(content);

    case TOKEN_EXCHANGE:
        // Process the token exchange request (RFC 8693)
        return handleTokenExchange(response);

    default:
        // This never happens.
        throw getApiCaller().unknownAction("/api/auth/token", action);
}

The following table lists response parameters related to Token Exchange in a response from the /auth/token API.

Response Parameter Type Description
resources string array The values of the resource request parameters.
audiences string array The values of the audience request parameters.
scopes string array The scopes listed in the scope request parameter.
requestedTokenType string A string representing the requested_token_type request parameter such as "ACCESS_TOKEN". (cf. TokenType.java)
subjectToken string The value of the subject_token request parameter.
subjectTokenType string A string representing the value of the subject_token_type request parameter such as "ACCESS_TOKEN". (cf. TokenType.java)
subjectTokenInfo object Information about the subject token. Available only when the token type is either access token or refresh token.
actorToken string The value of the actor_token request parameter.
actorTokenType string A string representing the value of the actor_token_type request parameter such as "ACCESS_TOKEN". (cf. TokenType.java)
actorTokenInfo object Information about the actor token. Available only when the token type is either access token or refresh token.

Token Validation

Regarding validation on input tokens, the specification states as follows. That is, the specification does not define details about token validation and leaves them to authorization server implementations.

In processing the request, the authorization server MUST perform the appropriate validation procedures for the indicated token type and, if the actor token is present, also perform the appropriate validation procedures for its indicated token type. The validity criteria and details of any particular token are beyond the scope of this document and are specific to the respective type of token and its content.

To reduce the burden of authorization servers, Authlete’s /auth/token API performs the following validation.

No. Validation
1 Confirm that the value of the requested_token_type request parameter is one of the registered token type identifiers if the request parameter is given and its value is not empty.
2 Confirm that the subject_token request parameter is given and its value is not empty.
3 Confirm that the subject_token_type request parameter is given and its value is one of the registered token type identifiers.
4 Confirm that the actor_token_type request parameter is given and its value is one of the registered token type identifiers if the actor_token request parameter is given and its value is not empty.
5 Confirm that the actor_token_type request parameter is not given or its value is empty when the actor_token request parameter is not given or its value is empty.

Furthermore, Authlete performs additional validation on the tokens specified by the subject_token request parameter and the actor_token request parameter according to their respective token types as shown below.

Token Validation for JWT

No. Validation
1 Confirm that the format conforms to the JWT specification (RFC 7519 JSON Web Token (JWT)).
2 Check if the JWT is encrypted and if it is encrypted, then (a) reject the token exchange request when the tokenExchangeEncryptedJwtRejected flag of the service is true or (b) skip remaining validation steps when the flag is false.
3 Confirm that the current time has not reached the time indicated by the exp claim if the JWT contains the claim.
4 Confirm that the current time is equal to or after the time indicated by the iat claim if the JWT contains the claim.
5 Confirm that the current time is equal to or after the time indicated by the nbf claim if the JWT contains the claim.
6 Check if the JWT is signed and if it is not signed, then (a) reject the token exchange request when the tokenExchangeUnsignedJwtRejected flag of the service is true or (b) finish validation on the input token.
7 (The signature of the JWT is not verified.)

Token Validation for Access Token

No. Validation
1 Confirm that the token is an access token that has been issued by the Authlete server of your service.
2 Confirm that the access token has not expired.
3 Confirm that the access token belongs to the service.

Token Validation for Refresh Token

No. Validation
1 Confirm that the token is a refresh token that has been issued by the Authlete server of your service.
2 Confirm that the refresh token has not expired.
3 Confirm that the refresh token belongs to the service.

Token Validation for ID Token

No. Validation
1 Confirm that the format conforms to the JWT specification (RFC 7519 JSON Web Token (JWT)).
2 Check if the ID Token is encrypted and if it is encrypted, then (a) reject the token exchange request when the tokenExchangeEncryptedJwtRejected flag of the service is true or (b) skip remaining validation steps when the flag is false.
3 Confirm that the ID Token contains the exp claim and the current time has not reached the time indicated by the claim.
4 Confirm that the ID Token contains the iat claim and the current time is equal to or after the time indicated by the claim.
5 Confirm that the current time is equal to or after the time indicated by the nbf claim if the ID Token contains the claim.
6 Confirm that the ID Token contains the iss claim and its value is a valid URI. In addition, confirm that the URI has the https scheme, no query component and no fragment component.
7 Confirm that the ID Token contains the aud claim and its value is a JSON string or an array of JSON strings.
8 Confirm that the value of the nonce claim is a JSON string if the ID Token contains the claim.
9 Check if the ID Token is signed and if it is not signed, then (a) reject the token exchange request when the tokenExchangeUnsignedJwtRejected flag of the service is true or (b) finish validation on the input token.
10 Confirm that the signature algorithm of the ID Token is asymmetric.
11 Verify the signature of the ID Token.

Token Validation for SAML 1.1 Assertion

No. Validation
1 (Authlete does not perform any validation for this token type.)

Token Validation for SAML 2.0 Assertion

No. Validation
1 (Authlete does not perform any validation for this token type.)

Configuration

As already mentioned, authorization server implementations have to complement RFC 8693 with their own rules for secure token exchange. For that purpose, Authlete provides some configuration options as listed below. Authorization server implementers may utilize them and/or implement their own rules.

Service Configuration

Configuration Option Type Description
Identifiable Clients Only boolean Whether to reject token exchange requests by unidentifiable clients - Service.tokenExchangeByIdentifiableClientsOnly (JavaDoc)
Confidential Clients Only boolean Whether to reject token exchange requests by public clients - Service.tokenExchangeByConfidentialClientsOnly (JavaDoc)
Permitted Clients Only boolean Whether to reject token exchange requests by clients that have no explicit permission - Service.tokenExchangeByPermittedClientsOnly (JavaDoc)
Encrypted JWT Rejected boolean Whether to reject token exchange requests which use encrypted JWTs as input tokens - Service.tokenExchangeEncryptedJwtRejected (JavaDoc)
Unsigned JWT Rejected boolean Whether to reject token exchange requests which use unsigned JWTs as input tokens - Service.tokenExchangeUnsignedJwtRejected (JavaDoc)

Client Extension Configuration

Configuration Type Description
Explicit Permission for Token Exchange boolean Whether to have an explicit permission for token exchange - ClientExtension.tokenExchangePermitted (JavaDoc)

Example

Authorization Server Implementation Example

TokenExchanger.java in java-oauth-server, an open-source sample implementation of authorization server written in Java, is a sample implementation of processing a token exchange request.

Note that the implementation is just an example and does not intend to be perfect for commercial use.

Request and Response Example

1. Prepare an ID Token in some way or other.

$ ID_TOKEN=eyJraWQiOiJhdXRobGV0ZS1mYXBpZGV2LWFwaS0yMDE4MDUyNCIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2ZhcGlkZXYtYXMuYXV0aGxldGUubmV0LyIsInN1YiI6IjEwMDQiLCJhdWQiOlsiNTg5OTQ2MzYxNDQ0ODA2MyJdLCJleHAiOjE2NTg2NzExMzAsImlhdCI6MTY1ODY3MDgzMCwiYXV0aF90aW1lIjoxNjU4NjcwODMwLCJub25jZSI6IjEyMzQ1Njc4OSIsInZlcmlmaWVkX2NsYWltcyI6eyJ2ZXJpZmljYXRpb24iOnsidHJ1c3RfZnJhbWV3b3JrIjoibmlzdF84MDBfNjNBIn0sImNsYWltcyI6eyJnaXZlbl9uYW1lIjoiSW5nYSIsImZhbWlseV9uYW1lIjoiU2lsdmVyc3RvbmUiLCJiaXJ0aGRhdGUiOiIxOTkxLTExLTA2IiwiOmFnZV8xOF9vcl9vdmVyIjpudWxsfX19.mxE8FQaDb0edY_rWasSQ7pEMXbFon7oWr-Ccv1dB15q8eh2MaKRGrgvwPw_XjAdXlMNzkcV6iEUjRUvLGTvzm7_45cdoOxRX1xWzQw-vwvRbM46xd3Yht3EVjyRUUBJ_92J1yBmu7Nn93rygcnCE-fC_bSTSIJWgEnoC7dpxHYnoJ2QHrIOYFMBAA_3ZYCLGpgiWbIZnB2D1ib2eqwJ9zoJqeFNEBhXo9ThYkASHYaG-ZWofy7364lgeV4Rqy1r4XqzchFRW4yzWs_IM72bTtXTUkstlNOxZU12KEz50uVhtcOXv06iI71I9vceRP-ZVICpq7Knt0vEKWTM41E3ziw

2. Make a token exchange request.

$ curl http://localhost:8080/api/token -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange -d subject_token=$ID_TOKEN -d subject_token_type=urn:ietf:params:oauth:token-type:id_token -d client_id=5908895171 -d scope=email

3. Receive a token exchange response.

{
  "access_token":"NEdL-q9EfOI4S5XzaMeimXAXVqS139Jm9DTYeLUAd5o",
  "issued_token_type":"urn:ietf:params:oauth:token-type:access_token",
  "token_type":"Bearer",
  "expires_in":86400,
  "scope":"email",
  "refresh_token":"pK9f5OWIrx58_XWrE_SpCLLN-BM673ljliTSffjqwao"
}