-
Notifications
You must be signed in to change notification settings - Fork 0
User guide: Create custom exchange
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.
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
}
}
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:
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();
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.
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 extendsAbstractOAuth2TokenResponseHandler
)
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.
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.
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.
It can look quite confusing for the first time, so you can always check source code for the components' implementations for common auth providers:
OAuth2TokenResponse
andOAuth2TokenResponse.Factory
implementations for GoogleOAuth2TokenResponseHandler
implementation for GoogleOAuth2Client
implementation for GoogleAuthorizationCodeExchange
implementation for Google
We also encourage you to check out official Java docs which also describe more technical details of all the provided components in a library ;)