Token exchange
Token exchange (RFC 8693) is an extension to OAuth 2.0 for implementing scenarios where one token needs to be swapped for another.
Some scenarios that may involve a token exchange:
Scenario | Example |
---|---|
Delegation (on behalf of) |
Issuing a service that is fulfilling some token-authorised request with a token to access a backend resource |
Impersonation (act as) |
Issuing a super user with a token to access a resource as some regular user |
The exchange occurs at the standard token endpoint of an authorisation server,
with a special grant type (urn:ietf:params:oauth:grant-type:token-exchange
)
established for the purpose.
The exchange protocol is designed for maximum flexibility. The submitted token and the newly minted token to be of any type:
- OAuth 2.0: Access token, refresh token
- OpenID Connect: ID token
- SAML 1.1 or 2.0 assertion
- Some arbitrary JWT
- Some other type of token
The following examples assume v9.17 of the SDK.
Access token exchange
The following snippet reproduces the example from the token exchange specification where a service needs to obtain a downstream token for some backend:
import java.net.*;
import java.util.*;
import com.nimbusds.oauth2.sdk.*;
import com.nimbusds.oauth2.sdk.auth.*;
import com.nimbusds.oauth2.sdk.http.*;
import com.nimbusds.oauth2.sdk.id.*;
import com.nimbusds.oauth2.sdk.token.*;
import com.nimbusds.oauth2.sdk.tokenexchange.*;
// The client credentials for a basic authentication
ClientID clientID = new ClientID("rs08");
Secret clientSecret = new Secret("eij8teegie3aequuQu9quahp7Vea7ohf");
ClientSecretBasic clientSecretBasic = new ClientSecretBasic(clientID, clientSecret);
// The upstream access token (must have been validated)
AccessToken accessToken = new BearerAccessToken("accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC");
// Compose the token exchange request
URI tokenEndpoint = new URI("https://as.example.com/as/token.oauth2");
URI resource = new URI("https://backend.example.com/api");
Scope scope = null; // default scope for resource
TokenRequest tokenRequest = new TokenRequest(
tokenEndpoint,
clientSecretBasic,
new TokenExchangeGrant(
accessToken,
TokenTypeURI.ACCESS_TOKEN),
scope,
Collections.singletonList(resource),
null);
// Send the token request
HTTPRequest httpRequest = tokenRequest.toHTTPRequest();
HTTPResponse httpResponse = httpRequest.send();
// Parse the token response
TokenResponse tokenResponse = TokenResponse.parse(httpResponse);
if (! tokenResponse.indicatesSuccess()) {
// The token request failed
ErrorObject errorObject = tokenResponse.toErrorResponse().getErrorObject();
System.out.println(errorObject.getHTTPStatusCode());
return;
}
AccessTokenResponse tokenSuccessResponse = tokenResponse.toSuccessResponse();
// Expecting access token of type Bearer
AccessToken downstreamToken = tokenSuccessResponse.getTokens().getAccessToken();
if (! AccessTokenType.BEARER.equals(downstreamToken.getType()) &&
! TokenTypeURI.ACCESS_TOKEN.equals(downstreamToken.getIssuedTokenType())) {
// Unexpected token type
System.out.println("Received unexpected token: " + downstreamToken.getIssuedTokenType());
return;
}
// Use the downstream token...