1. Introduction When it comes to adding , we realize not only that the configuration changes depending on which framework you are going to use, but that for each HTTP client you use, you must configure OAuth2 in a different way. authorization to call secured services For this reason, the simplest thing when implementing an authorization layer through OAuth2 to call those services, would be to outsource the generation of the tokens to a new personalized client. This way we would have a maintainable integration, isolated from the REST client we are using. This article guides you through the creation of a simple library which allows you to grant your HTTP requests with the required authorization token, and integrate in your services . whatever client you may use The authorization flow is described in the image above: Authorization request is sent from client to OAuth server. Access token is returned to the client. Access token is then sent from client to the API service (acting as resource server) on each request for a protected resource access. Resource server checks the token with the OAuth server to confirm that the client is authorized to consume that resource. Server responds with requested protected resources. 2. Setting up the required dependencies We will need a few libraries to build our custom OAuth2 client. First of all, the client library, which will provide us with the HTTP client for the integration with the authorization server, as well as a toolset for the request building. So it would be the core library for our client. Apache HTTP In the second one, we find another Apache library, called . In this case, this dependency would be optional, since we only need a set of predefined values in the OAuth2 Protocol definition, gathered in the class. We could also define those values by ourselves, to get rid of this dependency. cxf-rt-rs-security-oauth2 OAuthConstants Lastly, we include the library. This library is a helpful toolset when we are handling JSON data. It is really useful to parse and manipulate JSON in Java. json <dependencies> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-rs-security-oauth2</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20160212</version> </dependency> </dependencies> 3. Building the OAuth2 request We have to build the request to the server which will authorize our service as a granted client. To achieve this, we need to define the OAuth2 configuration we are using, including the , the authorization , the for the given grant type, and the for the resource we are requesting. grant type server URL credentials scope class OAuth2Config { String grantType; String clientId; String clientSecret; String username; String password; String accessTokenUri; String scope; } Once we have the configuration values initialized, we can use them to build the HTTP request for the authorization server. Typically, the HTTP method used to get the access token, will be a POST, as defined in the : OAuth 2.0 Authorization Protocol specification The client MUST use the HTTP "POST" method when making access token requests. Depending on the grant type we define, we must define different parameters on the POST request. We will use a list of to gather all those needed parameters. NameValuePair HttpUriRequest buildRequest() { List<NameValuePair> formData = new ArrayList<>(); formData.add(new BasicNameValuePair(GRANT_TYPE, config.getGrantType())); if (config.getScope() != null && !config.getScope().isBlank()) { formData.add(new BasicNameValuePair(SCOPE, config.getScope())); } if (CLIENT_CREDENTIALS_GRANT.equals(config.getGrantType())) { formData.add(new BasicNameValuePair(CLIENT_ID, config.getClientId())); formData.add(new BasicNameValuePair(CLIENT_SECRET, config.getClientSecret())); } if (RESOURCE_OWNER_GRANT.equals(config.getGrantType())) { formData.add(new BasicNameValuePair(RESOURCE_OWNER_NAME, config.getUsername())); formData.add(new BasicNameValuePair(RESOURCE_OWNER_PASSWORD, config.getPassword())); } return RequestBuilder.create(HttpPost.METHOD_NAME) .setUri(config.getAccessTokenUri()) .setEntity(new UrlEncodedFormEntity(formData, StandardCharsets.UTF_8)) .build(); } 4. Executing the OAuth2 request Since we are building an OAuth2 client as basic as possible, we will use the default HTTP client from library, to send our request to the authorization server. Apache HTTP CloseableHttpResponse doRequest(HttpUriRequest request) { CloseableHttpClient httpClient = HttpClients.createDefault(); try { return httpClient.execute(request); } catch (IOException e) { throw new OAuth2ClientException("An error occurred executing the request.", e); } } Once we receive the response, we need to handle it, extracting the information we need for the access token. class OAuth2Response { HttpEntity httpEntity; } We should check for errors before parsing the content to get the access token. We can consider here errors in the credentials we defined, a wrong or malformed URL, or any internal error from the authorization server. OAuth2Response execute(HttpUriRequest request) { CloseableHttpResponse httpResponse = doRequest(request); HttpEntity httpEntity = httpResponse.getEntity(); int statusCode = httpResponse.getStatusLine() .getStatusCode(); if (statusCode >= 400) { throw new OAuth2ClientException(statusCode, httpEntity); } return new OAuth2Response(httpEntity); } We should not forget to close the to avoid the memory leakage. But it is pretty important to wait until it is read properly, since it contains an InputStream which would become inaccessible once we have closed it. httpResponse Typically, the response content will come on a JSON format, with the access token data in a key-value schema. However, we should consider a server handling the data in a different format, like XML or URL encoded. For the scope of this article, we will consider our authorization server giving us JSON formatted content. The library we included earlier, will help us on the deserialization. org.json:json JSONObject handleResponse(HttpEntity entity) { String content = extractEntityContent(entity); String contentType = Optional.ofNullable(entity.getContentType()) .map(Header::getValue) .orElse(APPLICATION_JSON.getMimeType()); return new JSONObject(content); } String extractEntityContent(HttpEntity entity) { try { return EntityUtils.toString(entity, StandardCharsets.UTF_8); } catch (IOException e) { throw new OAuth2ClientException("An error occurred while extracting entity content.", e); } } Given the , it becomes much easier to handle the response, since we can retrieve instantly each value we are interested in. JSONObject 5. Putting all together The goal here is to obtain an access token to call the secured services we need. However, sometimes we also need to know some additional data, like the timestamp when the token is going to expire, the token type we are receiving, or the refresh token in the case the grant type is defined so. class AccessToken { long expiresIn; String tokenType; String refreshToken; String accessToken; AccessToken(JSONObject jsonObject) { expiresIn = jsonObject.optLong(ACCESS_TOKEN_EXPIRES_IN); tokenType = jsonObject.optString(ACCESS_TOKEN_TYPE); refreshToken = jsonObject.optString(REFRESH_TOKEN); accessToken = jsonObject.optString(ACCESS_TOKEN); } } Finally, we will get a client which will retrieve the access token data needed to grant our calls to the services, based on the configuration we defined. AccessToken accessToken() { HttpUriRequest request = buildRequest(); OAuth2Response response = execute(request); return new AccessToken(handleResponse(response.getHttpEntity())); } 6. Put into practice But, how could we integrate this custom client in our service? Well, as I mentioned at the beginning of the article, the idea of this custom OAuth2 client is to be we are using to consume the secured services. isolated from the framework and/or the HTTP client So I will show you a few examples of how to integrate it in different service environments. 6.1. Spring Framework - WebClient class WebClientConfig { @Bean(name = "securedWebClient") WebClient fetchWebClient(@Value("${host}") String host, OAuth2Config oAuth2Config) { OAuth2Client oAuth2Client = OAuth2Client.withConfig(oAuth2Config).build(); return WebClient.builder() .filter(new OAuth2ExchangeFilter(oAuth2Client)) .baseUrl(host) .build(); } @Bean @ConfigurationProperties(prefix = "security.oauth2.config") OAuth2Config oAuth2Config() { return new OAuth2Config(); } class OAuth2ExchangeFilter implements ExchangeFilterFunction { OAuth2Client oAuth2Client; @Override public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) { String token = Optional.ofNullable(oAuth2Client.accessToken()) .map(AccessToken::getAccessToken) .map("Bearer "::concat) .orElseThrow(() -> new AccessDeniedException()); ClientRequest newRequest = ClientRequest.from(request) .header(HttpHeaders.AUTHORIZATION, token) .build(); return next.exchange(newRequest); } } } 6.2. Spring Framework - Feign Client class FeignClientConfig { @Bean OAuthRequestInterceptor repositoryClientOAuth2Interceptor(OAuth2Client oAuth2Client) { return new OAuthRequestInterceptor(oAuth2Client); } class OAuthRequestInterceptor implements RequestInterceptor { OAuth2Client oAuth2Client; @Override public void apply(RequestTemplate requestTemplate) { String authToken = Optional.ofNullable(oAuth2Client.accessToken()) .map(AccessToken::getAccessToken) .map("Bearer "::concat) .orElseThrow(() -> new AccessDeniedException()); requestTemplate.header(HttpHeaders.AUTHORIZATION, authToken); } } } 6.3. Vert.x - Web Client class ProtectedResourceHandler implements Handler<RoutingContext> { OAuth2Config oAuth2Config; ProtectedResourceHandler() { // Resource handler initialization oAuth2Config = oauth2Config(config); } private OAuth2Config oauth2Config(JsonObject oauth2Properties) { return OAuth2Config.builder() .grantType(oauth2Properties.getString("grantType")) .accessTokenUri(oauth2Properties.getString("accessTokenUri")) .clientId(oauth2Properties.getString("clientId")) .clientSecret(oauth2Properties.getString("clientSecret")) .username(oauth2Properties.getString("username")) .password(oauth2Properties.getString("password")) .scope(oauth2Properties.getString("scope")) .build(); } @Override public void handle(RoutingContext routingContext) { WebClient.create(routingContext.vertx()) .getAbs(host) .uri(endpoint) .putHeader(HttpHeaders.AUTHORIZATION.toString(), generateToken()) .send() .onSuccess(httpResponse -> { /* Successful response handler */ }) .onFailure(err -> { /* Error response handler */ }); } String generateToken() { return Optional.of(OAuth2Client.withConfig(oAuth2Config).build()) .map(OAuth2Client::accessToken) .map(AccessToken::getAccessToken) .map("Bearer "::concat) .orElseThrow(() -> new AccessDeniedException()); } } 6.4. Quarkus - RESTEasy @RegisterRestClient @RegisterClientHeaders(SecurityHeaderFactory.class) interface DocumentClient { // External endpoints definition } class SecurityHeaderFactory implements ClientHeadersFactory { OAuth2Client oAuth2Client; @Inject SecurityHeaderFactory(OAuth2Properties oAuth2Properties) { oAuth2Client = OAuth2Client .withConfig(oauth2Config(oAuth2Properties)) .build(); } @Override public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders, MultivaluedMap<String, String> outgoingHeaders) { outgoingHeaders.add(HttpHeaders.AUTHORIZATION.toString(), generateToken()); return outgoingHeaders; } String generateToken() { return Optional.of(oAuth2Client) .map(OAuth2Client::accessToken) .map(AccessToken::getAccessToken) .map("Bearer "::concat) .orElseThrow(() -> new AccessDeniedException()); } OAuth2Config oauth2Config(OAuth2Properties oAuth2Properties) { return OAuth2Config.builder() .grantType(oAuth2Properties.getGrantType()) .accessTokenUri(oAuth2Properties.getAccessTokenUri()) .clientId(oAuth2Properties.getClientId()) .clientSecret(oAuth2Properties.getClientSecret()) .username(oAuth2Properties.getUsername()) .password(oAuth2Properties.getPassword()) .scope(oAuth2Properties.getScope()) .build(); } } 7. Conclusion In this article, we have seen how we can set up a , and how we can integrate it in your REST calls to retrieve a secured resource from an external service. simple OAuth2 Client You can check the code used for the OAuth2 Client, the repository is available over on . Github Also published on: https://malkomich.github.io/authenticating-rest-services-with-oauth2/