Repository: gravitee-io/graviteeio-access-management
Pull request #934 (2020-10-09 09:09:24)

feat(proxy): add http proxy support for all identity providers
Closes gravitee-io/issues#4396


Patch:
diff --git a/gravitee-am-gateway/gravitee-am-gateway-standalone/gravitee-am-gateway-standalone-distribution/src/main/resources/config/gravitee.yml b/gravitee-am-gateway/gravitee-am-gateway-standalone/gravitee-am-gateway-standalone-distribution/src/main/resources/config/gravitee.yml
index e1cd95bf74..dd6e62d560 100644
--- a/gravitee-am-gateway/gravitee-am-gateway-standalone/gravitee-am-gateway-standalone-distribution/src/main/resources/config/gravitee.yml
+++ b/gravitee-am-gateway/gravitee-am-gateway-standalone/gravitee-am-gateway-standalone-distribution/src/main/resources/config/gravitee.yml
@@ -187,6 +187,22 @@ handlers:
 gateway:
   url: http://localhost:8092
 
+# global configuration of the http client
+#httpClient:
+#  timeout: 10000 # in milliseconds
+#  proxy:
+#    type: HTML #HTTP, SOCK4, SOCK5
+#    http:
+#      host: localhost
+#      port: 3128
+#      username: user
+#      password: secret
+#    https:
+#      host: localhost
+#      port: 3128
+#      username: user
+#      password: secret
+
 # Sharding tags configuration
 # Allows to define inclusion/exclusion sharding tags to only deploy a part of security domains. To exclude just prefix the tag with '!'.
 #tags: public,private,!partner
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/spring/GithubAuthenticationProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/spring/GithubAuthenticationProviderConfiguration.java
index c3419dcef7..27f1ede1a0 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/spring/GithubAuthenticationProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/spring/GithubAuthenticationProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.github.authentication.spring;
 
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderConfiguration;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.ext.web.client.WebClientOptions;
 import io.vertx.reactivex.core.Vertx;
 import io.vertx.reactivex.ext.web.client.WebClient;
@@ -39,15 +40,21 @@ public class GithubAuthenticationProviderConfiguration {
     @Autowired
     private GithubIdentityProviderConfiguration configuration;
 
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
+
     @Bean
     @Qualifier("gitHubWebClient")
-    public WebClient httpClient() {
+    public WebClient httpClient(WebClientBuilder webClientBuilder) {
         WebClientOptions httpClientOptions = new WebClientOptions();
         httpClientOptions
                 .setUserAgent(DEFAULT_USER_AGENT)
                 .setConnectTimeout(configuration.getConnectTimeout())
-                .setMaxPoolSize(configuration.getMaxPoolSize());
+                .setMaxPoolSize(configuration.getMaxPoolSize())
+                .setSsl(true); // TLS is mandatory for GitHub IdP.
 
-        return WebClient.create(vertx, httpClientOptions);
+        return webClientBuilder.createWebClient(vertx, httpClientOptions);
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/main/java/io/gravitee/am/identityprovider/http/authentication/spring/HttpAuthenticationProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/main/java/io/gravitee/am/identityprovider/http/authentication/spring/HttpAuthenticationProviderConfiguration.java
index 5eeb80a873..47e90e5212 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/main/java/io/gravitee/am/identityprovider/http/authentication/spring/HttpAuthenticationProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/main/java/io/gravitee/am/identityprovider/http/authentication/spring/HttpAuthenticationProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.http.authentication.spring;
 
 import io.gravitee.am.identityprovider.http.configuration.HttpIdentityProviderConfiguration;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.ext.web.client.WebClientOptions;
 import io.vertx.reactivex.core.Vertx;
 import io.vertx.reactivex.ext.web.client.WebClient;
@@ -39,21 +40,28 @@ public class HttpAuthenticationProviderConfiguration {
     @Autowired
     private HttpIdentityProviderConfiguration configuration;
 
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
+
     @Bean
     @Qualifier("idpHttpAuthWebClient")
-    public WebClient httpClient() {
+    public WebClient httpClient(WebClientBuilder webClientBuilder) {
         WebClientOptions httpClientOptions = new WebClientOptions();
         httpClientOptions
                 .setUserAgent(DEFAULT_USER_AGENT)
                 .setConnectTimeout(configuration.getConnectTimeout())
                 .setMaxPoolSize(configuration.getMaxPoolSize());
 
-        if (configuration.getAuthenticationResource().getBaseURL() != null
-                && configuration.getAuthenticationResource().getBaseURL().startsWith("https://")) {
+        final boolean ssl = configuration.getAuthenticationResource().getBaseURL() != null
+                && configuration.getAuthenticationResource().getBaseURL().startsWith("https://");
+        if (ssl) {
             httpClientOptions.setSsl(true);
             httpClientOptions.setTrustAll(true);
         }
 
-        return WebClient.create(vertx, httpClientOptions);
+
+        return webClientBuilder.createWebClient(vertx, httpClientOptions);
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/spring/OAuth2GenericAuthenticationProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/spring/OAuth2GenericAuthenticationProviderConfiguration.java
index b14a0c5516..b67f50d09f 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/spring/OAuth2GenericAuthenticationProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/spring/OAuth2GenericAuthenticationProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.oauth2.authentication.spring;
 
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderConfiguration;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.ext.web.client.WebClientOptions;
 import io.vertx.reactivex.core.Vertx;
 import io.vertx.reactivex.ext.web.client.WebClient;
@@ -32,6 +33,7 @@ import org.springframework.context.annotation.Configuration;
 public class OAuth2GenericAuthenticationProviderConfiguration {
 
     private static final String DEFAULT_USER_AGENT = "Gravitee.io-AM/3";
+    private static final String HTTPS = "https://";
 
     @Autowired
     private Vertx vertx;
@@ -39,15 +41,34 @@ public class OAuth2GenericAuthenticationProviderConfiguration {
     @Autowired
     private OAuth2GenericIdentityProviderConfiguration configuration;
 
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
+
     @Bean
     @Qualifier("oauthWebClient")
-    public WebClient httpClient() {
+    public WebClient httpClient(WebClientBuilder webClientBuilder) {
         WebClientOptions httpClientOptions = new WebClientOptions();
         httpClientOptions
                 .setUserAgent(DEFAULT_USER_AGENT)
                 .setConnectTimeout(configuration.getConnectTimeout())
-                .setMaxPoolSize(configuration.getMaxPoolSize());
+                .setMaxPoolSize(configuration.getMaxPoolSize())
+                .setSsl(isTLS());
+
+        return webClientBuilder.createWebClient(vertx, httpClientOptions);
+    }
 
-        return WebClient.create(vertx, httpClientOptions);
+    /**
+     * Check if all defined oauth2 urls are secured or not.
+     * This method is mainly used to determine if ssl should be enabled on the webClient used to communicate with the oauth2 server.
+     *
+     * @return <code>true</code> if all urls are secured, <code>false</code> else.
+     */
+    private boolean isTLS() {
+        return configuration.getAccessTokenUri() != null && configuration.getAccessTokenUri().startsWith(HTTPS)
+                && configuration.getUserAuthorizationUri() != null && configuration.getUserAuthorizationUri().startsWith(HTTPS)
+                && configuration.getUserProfileUri() != null && configuration.getUserProfileUri().startsWith(HTTPS)
+                && configuration.getWellKnownUri() != null && configuration.getWellKnownUri().startsWith(HTTPS);
     }
 }
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/http/WebClientBuilder.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/http/WebClientBuilder.java
new file mode 100644
index 0000000000..745d9d48de
--- /dev/null
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/http/WebClientBuilder.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.gravitee.am.service.http;
+
+import io.vertx.core.net.ProxyOptions;
+import io.vertx.core.net.ProxyType;
+import io.vertx.ext.web.client.WebClientOptions;
+import io.vertx.reactivex.core.Vertx;
+import io.vertx.reactivex.ext.web.client.WebClient;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.net.URL;
+
+/**
+ * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public class WebClientBuilder {
+
+    private static final String HTTPS_SCHEME = "https";
+
+    @Value("${httpClient.timeout:10000}")
+    private int httpClientTimeout;
+
+    @Value("${httpClient.proxy.type:HTTP}")
+    private String httpClientProxyType;
+
+    @Value("${httpClient.proxy.http.host:#{systemProperties['http.proxyHost'] ?: 'localhost'}}")
+    private String httpClientProxyHttpHost;
+
+    @Value("${httpClient.proxy.http.port:#{systemProperties['http.proxyPort'] ?: 3128}}")
+    private int httpClientProxyHttpPort;
+
+    @Value("${httpClient.proxy.http.username:#{null}}")
+    private String httpClientProxyHttpUsername;
+
+    @Value("${httpClient.proxy.http.password:#{null}}")
+    private String httpClientProxyHttpPassword;
+
+    @Value("${httpClient.proxy.https.host:#{systemProperties['https.proxyHost'] ?: 'localhost'}}")
+    private String httpClientProxyHttpsHost;
+
+    @Value("${httpClient.proxy.https.port:#{systemProperties['https.proxyPort'] ?: 3128}}")
+    private int httpClientProxyHttpsPort;
+
+    @Value("${httpClient.proxy.https.username:#{null}}")
+    private String httpClientProxyHttpsUsername;
+
+    @Value("${httpClient.proxy.https.password:#{null}}")
+    private String httpClientProxyHttpsPassword;
+
+    @Value("#{systemProperties['httpClient.proxy'] == null ? false : true }")
+    private boolean isProxyConfigured;
+
+    public WebClient createWebClient(Vertx vertx, URL url) {
+
+        final int port = url.getPort() != -1 ? url.getPort() : (HTTPS_SCHEME.equals(url.getProtocol()) ? 443 : 80);
+
+        WebClientOptions options = new WebClientOptions()
+                .setDefaultPort(port)
+                .setDefaultHost(url.getHost())
+                .setKeepAlive(true)
+                .setMaxPoolSize(10)
+                .setTcpKeepAlive(true)
+                .setConnectTimeout(httpClientTimeout)
+                .setSsl(url.getProtocol().equals(HTTPS_SCHEME));
+
+        return createWebClient(vertx, options);
+    }
+
+    public WebClient createWebClient(Vertx vertx, WebClientOptions options) {
+
+        setProxySettings(options);
+        return WebClient.create(vertx, options);
+    }
+
+    private void setProxySettings(WebClientOptions options) {
+
+        if (this.isProxyConfigured) {
+            ProxyOptions proxyOptions = new ProxyOptions();
+            proxyOptions.setType(ProxyType.valueOf(httpClientProxyType));
+            if (options.isSsl()) {
+                proxyOptions.setHost(httpClientProxyHttpsHost);
+                proxyOptions.setPort(httpClientProxyHttpsPort);
+                proxyOptions.setUsername(httpClientProxyHttpsUsername);
+                proxyOptions.setPassword(httpClientProxyHttpsPassword);
+            } else {
+                proxyOptions.setHost(httpClientProxyHttpHost);
+                proxyOptions.setPort(httpClientProxyHttpPort);
+                proxyOptions.setUsername(httpClientProxyHttpUsername);
+                proxyOptions.setPassword(httpClientProxyHttpPassword);
+            }
+            options.setProxyOptions(proxyOptions);
+        }
+    }
+}
diff --git a/gravitee-am-service/src/main/java/io/gravitee/am/service/spring/WebClientsConfiguration.java b/gravitee-am-service/src/main/java/io/gravitee/am/service/spring/WebClientsConfiguration.java
index 8b7a1b4026..0fdebc43f0 100644
--- a/gravitee-am-service/src/main/java/io/gravitee/am/service/spring/WebClientsConfiguration.java
+++ b/gravitee-am-service/src/main/java/io/gravitee/am/service/spring/WebClientsConfiguration.java
@@ -15,19 +15,15 @@
  */
 package io.gravitee.am.service.spring;
 
-import io.vertx.core.net.ProxyOptions;
-import io.vertx.core.net.ProxyType;
-import io.vertx.ext.web.client.WebClientOptions;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.reactivex.core.Vertx;
 import io.vertx.reactivex.ext.web.client.WebClient;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Lazy;
 
 import java.net.MalformedURLException;
 import java.net.URI;
-import java.net.URL;
 
 /**
  * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
@@ -36,87 +32,24 @@ import java.net.URL;
 @Configuration
 public class WebClientsConfiguration {
 
-    private static final String HTTPS_SCHEME = "https";
-
-    @Value("${httpClient.timeout:10000}")
-    private int httpClientTimeout;
-
-    @Value("${httpClient.proxy.type:HTTP}")
-    private String httpClientProxyType;
-
-    @Value("${httpClient.proxy.http.host:#{systemProperties['http.proxyHost'] ?: 'localhost'}}")
-    private String httpClientProxyHttpHost;
-
-    @Value("${httpClient.proxy.http.port:#{systemProperties['http.proxyPort'] ?: 3128}}")
-    private int httpClientProxyHttpPort;
-
-    @Value("${httpClient.proxy.http.username:#{null}}")
-    private String httpClientProxyHttpUsername;
-
-    @Value("${httpClient.proxy.http.password:#{null}}")
-    private String httpClientProxyHttpPassword;
-
-    @Value("${httpClient.proxy.https.host:#{systemProperties['https.proxyHost'] ?: 'localhost'}}")
-    private String httpClientProxyHttpsHost;
-
-    @Value("${httpClient.proxy.https.port:#{systemProperties['https.proxyPort'] ?: 3128}}")
-    private int httpClientProxyHttpsPort;
-
-    @Value("${httpClient.proxy.https.username:#{null}}")
-    private String httpClientProxyHttpsUsername;
-
-    @Value("${httpClient.proxy.https.password:#{null}}")
-    private String httpClientProxyHttpsPassword;
-
-    @Value("#{systemProperties['httpClient.proxy'] == null ? false : true }")
-    private boolean isProxyConfigured;
-
     @Value("${reCaptcha.serviceUrl:https://www.google.com/recaptcha/api/siteverify}")
     private String recaptchaServiceUrl;
 
     @Value("${newsletter.url:https://newsletter.gravitee.io}")
     private String newsletterURL;
 
-    @Bean("recaptchaWebClient")
-    protected WebClient recaptchaWebClient(Vertx vertx) throws MalformedURLException {
-        return createWebClient(vertx, URI.create(recaptchaServiceUrl).toURL());
+    @Bean
+    protected WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
     }
 
-    @Bean("newsletterWebClient")
-    protected WebClient newsletterWebClient(Vertx vertx) throws MalformedURLException {
-        return createWebClient(vertx, URI.create(newsletterURL).toURL());
+    @Bean("recaptchaWebClient")
+    protected WebClient recaptchaWebClient(Vertx vertx, WebClientBuilder webClientBuilder) throws MalformedURLException {
+        return webClientBuilder.createWebClient(vertx, URI.create(recaptchaServiceUrl).toURL());
     }
 
-    private WebClient createWebClient(Vertx vertx, URL url) {
-
-        final int port = url.getPort() != -1 ? url.getPort() : (HTTPS_SCHEME.equals(url.getProtocol()) ? 443 : 80);
-
-        WebClientOptions options = new WebClientOptions()
-                .setDefaultPort(port)
-                .setDefaultHost(url.getHost())
-                .setKeepAlive(true)
-                .setMaxPoolSize(10)
-                .setTcpKeepAlive(true)
-                .setConnectTimeout(httpClientTimeout)
-                .setSsl(url.getProtocol().equals(HTTPS_SCHEME));
-
-        if (this.isProxyConfigured) {
-            ProxyOptions proxyOptions = new ProxyOptions();
-            proxyOptions.setType(ProxyType.valueOf(httpClientProxyType));
-            if (HTTPS_SCHEME.equals(url.getProtocol())) {
-                proxyOptions.setHost(httpClientProxyHttpsHost);
-                proxyOptions.setPort(httpClientProxyHttpsPort);
-                proxyOptions.setUsername(httpClientProxyHttpsUsername);
-                proxyOptions.setPassword(httpClientProxyHttpsPassword);
-            } else {
-                proxyOptions.setHost(httpClientProxyHttpHost);
-                proxyOptions.setPort(httpClientProxyHttpPort);
-                proxyOptions.setUsername(httpClientProxyHttpUsername);
-                proxyOptions.setPassword(httpClientProxyHttpPassword);
-            }
-            options.setProxyOptions(proxyOptions);
-        }
-
-        return WebClient.create(vertx, options);
+    @Bean("newsletterWebClient")
+    protected WebClient newsletterWebClient(Vertx vertx, WebClientBuilder webClientBuilder) throws MalformedURLException {
+        return webClientBuilder.createWebClient(vertx, URI.create(newsletterURL).toURL());
     }
 }


Test patch:
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTestConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTestConfiguration.java
index 66c157c46a..de93796c89 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTestConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTestConfiguration.java
@@ -19,6 +19,7 @@ import io.gravitee.am.identityprovider.api.AuthenticationProvider;
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderConfiguration;
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderMapper;
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderRoleMapper;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.reactivex.core.Vertx;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -69,4 +70,9 @@ public class GithubAuthenticationProviderTestConfiguration {
     public GithubIdentityProviderMapper mapper() {
         return new GithubIdentityProviderMapper();
     }
+
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/test/java/io/gravitee/am/identityprovider/http/authentication/HttpAuthenticationProviderTestConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/test/java/io/gravitee/am/identityprovider/http/authentication/HttpAuthenticationProviderTestConfiguration.java
index 097d5e710c..cdfe073c90 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/test/java/io/gravitee/am/identityprovider/http/authentication/HttpAuthenticationProviderTestConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-http/src/test/java/io/gravitee/am/identityprovider/http/authentication/HttpAuthenticationProviderTestConfiguration.java
@@ -21,6 +21,7 @@ import io.gravitee.am.identityprovider.http.HttpIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.http.configuration.HttpIdentityProviderConfiguration;
 import io.gravitee.am.identityprovider.http.configuration.HttpResourceConfiguration;
 import io.gravitee.am.identityprovider.http.configuration.HttpResponseErrorCondition;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.gravitee.common.http.HttpHeader;
 import io.gravitee.common.http.HttpMethod;
 import io.vertx.core.json.JsonObject;
@@ -83,4 +84,9 @@ public class HttpAuthenticationProviderTestConfiguration {
     public Vertx vertx() {
         return Vertx.vertx();
     }
+
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTestConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTestConfiguration.java
index 5c558fca19..18edc2b7a3 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTestConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTestConfiguration.java
@@ -19,6 +19,7 @@ import io.gravitee.am.identityprovider.api.AuthenticationProvider;
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderConfiguration;
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderMapper;
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderRoleMapper;
+import io.gravitee.am.service.http.WebClientBuilder;
 import io.vertx.reactivex.core.Vertx;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -69,4 +70,9 @@ public class OAuth2GenericAuthenticationProviderTestConfiguration {
     public Vertx vertx() {
         return Vertx.vertx();
     }
+
+    @Bean
+    public WebClientBuilder webClientBuilder() {
+        return new WebClientBuilder();
+    }
 }