Skip to content

User guide: Create custom exchange

Marcin Zielonka edited this page Mar 10, 2022 · 5 revisions

About

Apart from the ready-to-use templates for most common auth providers, the library also provides some generic templates which can be used to create an exchange for a custom provider.

Build exchange from scratch

You can always create an exchange for a custom auth provider totally from scratch. Then, you need to create a class for the exchange that implements AuthorizationCodeExchange - to provide implementation for AuthorizationCodeExchange#exchangeAuthorizationCode method.

public class CustomAuthorizationCodeExchange implements AuthorizationCodeExchange {

    @Override
    public OAuth2TokenResponse exchangeAuthorizationCode(@NotNull String code) {
        // your custom implementation here
    }

}

Use ready generic templates

Although implementing a custom exchange totally from scratch provides the biggest flexibility - it can be quite time-consuming and can require lots of code (as you would need to implement a custom HTTP client, handling response, etc.). Therefore, the library provides some generic templates which can be used to create a custom exchange by providing only essential information.

In order to create a custom exchange from generic template:

Step 1: Create a class for exchange that extends AbstractAuthorizationCodeExchange:

public class CustomAuthorizationCodeExchange extends AbstractAuthorizationCodeExchange {

    public CustomAuthorizationCodeExchange(@NotNull OkHttpClient httpClient,
                                           @NotNull OAuth2Client oAuth2Client,
                                           @NotNull OAuth2TokenResponseHandler responseHandler) {
        super(httpClient, oAuth2Client, responseHandler);
    }

    @Override
    public OAuth2TokenResponse exchangeAuthorizationCode(@NotNull String code) {
         verifyAuthorizationCode(code);

         // Build HTTP request here

         return this.makeHttpCall(request);
    }

}

As you can see here, the AbstractAuthorizationCodeExchange requires three components that are used during the exchange:

  • OkHttpClient httpClient
  • OAuth2Client oAuth2Client
  • OAuth2TokenResponseHandler responseHandler

The httpClient is the HTTP client implementation from OkHttp library and it is used to make an HTTP call to the authorization server to issue a token from the given authorization code. Regarding the oAuth2Client and responseHandler - we will focus on them later.

You also need to implement AuthorizationCodeExchange#exchangeAuthorizationCode method. However, you can use some methods provided by the abstract exchange which make the method implementation much easier.

First of all, you can call verifyAuthorizationCode method which ensure that the provided code is not null and not empty (otherwise, the proper exception will be thrown).

Then you need to create Request instance (from OkHttp library) which represents the HTTP request made to the authorization server. It must be implemented by yourself as this is an exchange-specific part of the exchange. You can take pattern of the example below representing the HTTP POST request with x-www-urlencoded content:

var requestBody = new FormBody.Builder()
        .add("client_id", this.oAuth2Client.getClientId())
        .add("client_secret", this.oAuth2Client.getClientSecret())
        .add("code", code)
        .add("grant_type", "authorization_code")
        .add("redirect_uri", this.oAuth2Client.getRedirectUri())
        .build();

var request = new Request.Builder()
         .url(this.oAuth2Client.getTokenUrl())
         .post(requestBody)
         .build();

Step 2: Create an OAuthClient:

As you can notice, credentials used during building an HTTP request are stored in the OAuth2Client instance passed during creating the exchange instance. The OAuth2Client is an interface that provides four methods:

  • getClientId - returns the OAuth2 client ID
  • getClientSecret - returns the client secret associated with the client ID
  • getRedirectUri - returns the redirection URI used during authorization code flow
  • getTokenUrl - returns the token URL which the request for exchange authorization code for a token is sent to

You can use AbstractOAuth2Client class which implements the first three methods, so you need to implement the getTokenUrl method on your own.

Step 3: Create OAuth2TokenResponseHandler:

The OAuth2TokenResponseHandler is an interface responsible for handling the HTTP response received from authorization server after issuing a token from given authorization code. The handler should manage both successful and error response to make the exchange work properly in all cases. The interface provides one generic method handleResponse(@NotNull Response response) which returns `OAuth2TokenResponse instance holding received data from authorization server.

As always, you can implement the handler totally from scratch by creating a class which implements the mentioned interface. However, the core library provides also two generic templates for response handlers:

  • AbstractOAuth2TokenResponseHandler
  • AbstractJsonBodyOAuth2TokenResponseHandler (which extends AbstractOAuth2TokenResponseHandler)

AbstractOAuth2TokenResponseHandler

This template implements handleResponse method in the way that it delegates the responsibility to two methods (which have to be explicitly implemented):

  • handleErrorResponse(@NotNull Response response)
  • handleSuccessfulResponse(@NotNull Response response)

The fact whether the incoming HTTP response is successful or not is determined by Response#isSuccessful() method.

AbstractJsonBodyOAuth2TokenResponseHandler

This template extends the AbstractOAuth2TokenResponseHandler (so response handling is split into successful and error one) but also maps the incoming successful response body (which has to be in JSON format) into the Java map object by default. Therefore, this template requires also additional ObjectMapper and OAuth2TokenResponse.Factory instances which are responsible for converting the response body to the map and then to OAuth2TokenResponse object. Regarding the error message response, this template throws an `ExchangeException with HTTP status message by default.

Step 4: Create OAuth2TokenResponse:

You also need to create a class that implements OAuth2TokenResponse - it is an interface which provides accessors for fields that should be returned in an HTTP response from authorization server according to Section 5 of RFC6749 document. Therefore, you can find the following methods there:

  • getAccessToken()
  • getTokenType()
  • getExpiresIn()
  • getRefreshToken()
  • getScope()

Due to the fact that most authorization providers return the token response in JSON format which can be easily converted into the Java map - the core library provides an abstract template for OAuth2TokenResponse which also implements a Java Map<String, Object> interface (it uses HashMap delegate under the hood). Therefore, it can be easily used for retrieving both the required and additional, auth-provider-specific data returned in a response.

Summary

It can look quite confusing for the first time, so you can always check source code for the components' implementations for common auth providers:

We also encourage you to check out official Java docs which also describe more technical details of all the provided components in a library ;)