Repository: gravitee-io/graviteeio-access-management
Pull request #903 (2020-09-02 14:09:12)

refactor(identity-provider): create common class hierarchy to social …
…and OpenID Connect IdP

fix gravitee-io/issues#4108

tcompiegne: 👍 

Patch:
diff --git a/Makefile b/Makefile
index 48fe15b523..911c4c4d2c 100644
--- a/Makefile
+++ b/Makefile
@@ -288,6 +288,7 @@ pluginsGateway: # Copy plugins to Gateway
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-ldap/target/gravitee-am-identityprovider-ldap-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-mongo/target/gravitee-am-identityprovider-mongo-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/target/gravitee-am-identityprovider-oauth2-generic-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
+	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/target/gravitee-am-identityprovider-azure-ad-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
 	@cp -fr gravitee-am-reporter/gravitee-am-reporter-mongodb/target/gravitee-am-reporter-mongodb-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
 	@cp -fr gravitee-am-repository/gravitee-am-repository-mongodb/target/gravitee-am-repository-mongodb-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
 	@cp -fr gravitee-am-factor/gravitee-am-factor-otp/target/gravitee-am-factor-otp-$(GIO_AM_VERSION).zip $(GIO_AM_GATEWAY_PLUGINS)
@@ -306,6 +307,7 @@ pluginsManagement: # Copy plugins to Management API
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/target/gravitee-am-identityprovider-oauth2-generic-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/target/gravitee-am-identityprovider-oauth2-generic-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
 	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/target/gravitee-am-identityprovider-oauth2-generic-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
+	@cp -fr gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/target/gravitee-am-identityprovider-azure-ad-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
 	@cp -fr gravitee-am-reporter/gravitee-am-reporter-mongodb/target/gravitee-am-reporter-mongodb-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
 	@cp -fr gravitee-am-repository/gravitee-am-repository-mongodb/target/gravitee-am-repository-mongodb-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
 	@cp -fr gravitee-am-factor/gravitee-am-factor-otp/target/gravitee-am-factor-otp-$(GIO_AM_VERSION).zip $(GIO_AM_MANAGEMENT_API_PLUGINS)
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/pom.xml
index afea5ad9ea..28fcbe1042 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/pom.xml
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/pom.xml
@@ -46,6 +46,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>io.gravitee.am.jwt</groupId>
+            <artifactId>gravitee-am-jwt</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>io.gravitee.gateway</groupId>
             <artifactId>gravitee-gateway-api</artifactId>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderMapper.java
similarity index 90%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderMapper.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderMapper.java
index 10ad66e793..e257e69a52 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderMapper.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderMapper.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure;
+package io.gravitee.am.identityprovider.api;
 
 import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
 
@@ -23,7 +23,7 @@ import java.util.Map;
  * @author Eric LELEU (eric.leleu at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class AzureADIdentityProviderMapper implements IdentityProviderMapper {
+public class DefaultIdentityProviderMapper implements IdentityProviderMapper {
     private Map<String, String> mappers;
 
     public Map<String, String> getMappers() {
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderRoleMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderRoleMapper.java
similarity index 90%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderRoleMapper.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderRoleMapper.java
index b711fc1ddc..32aa1b5bad 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderRoleMapper.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/DefaultIdentityProviderRoleMapper.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure;
+package io.gravitee.am.identityprovider.api;
 
 import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
 
@@ -23,7 +23,7 @@ import java.util.Map;
  * @author Eric LELEU (eric.leleu at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class AzureADIdentityProviderRoleMapper implements IdentityProviderRoleMapper {
+public class DefaultIdentityProviderRoleMapper implements IdentityProviderRoleMapper {
     private Map<String, String[]> roles;
 
     public Map<String, String[]> getRoles() {
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/IdentityProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/IdentityProvider.java
index f2db58cdd6..56fef883b5 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/IdentityProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/IdentityProvider.java
@@ -32,10 +32,10 @@ public interface IdentityProvider {
     default Class<? extends UserProvider> userProvider() { return null; }
 
     default Class<? extends IdentityProviderMapper> mapper() {
-        return null;
+        return DefaultIdentityProviderMapper.class;
     }
 
     default Class<? extends IdentityProviderRoleMapper> roleMapper() {
-        return null;
+        return DefaultIdentityProviderRoleMapper.class;
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectAuthenticationProvider.java
index 6e8881d574..e59b2435cf 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectAuthenticationProvider.java
@@ -17,12 +17,15 @@ package io.gravitee.am.identityprovider.api.oidc;
 
 import io.gravitee.am.common.oidc.AuthenticationFlow;
 import io.gravitee.am.identityprovider.api.social.SocialAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
-public interface OpenIDConnectAuthenticationProvider extends SocialAuthenticationProvider {
+public interface OpenIDConnectAuthenticationProvider<T extends OpenIDConnectIdentityProviderConfiguration> extends SocialAuthenticationProvider {
+
+    T getConfiguration();
 
     AuthenticationFlow authenticationFlow();
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectIdentityProviderConfiguration.java
new file mode 100644
index 0000000000..194b671c1c
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/OpenIDConnectIdentityProviderConfiguration.java
@@ -0,0 +1,33 @@
+/**
+ * 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.identityprovider.api.oidc;
+
+import io.gravitee.am.common.jwt.SignatureAlgorithm;
+import io.gravitee.am.identityprovider.api.oidc.jwt.KeyResolver;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
+
+/**
+ * @author Eric LELEU (eric.leleu at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public interface OpenIDConnectIdentityProviderConfiguration extends SocialIdentityProviderConfiguration {
+    public String getWellKnownUri();
+    public boolean isUseIdTokenForUserInfo();
+    public KeyResolver getPublicKeyResolver();
+    public SignatureAlgorithm getSignatureAlgorithm();
+    public String getResolverParameter();
+    public boolean isEncodeRedirectUri();
+}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/JWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/JWKSourceResolver.java
similarity index 94%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/JWKSourceResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/JWKSourceResolver.java
index 39867cc73f..451937695f 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/JWKSourceResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/JWKSourceResolver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.jwks;
+package io.gravitee.am.identityprovider.api.oidc.jwt;
 
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.proc.SecurityContext;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/KeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyProcessor.java
similarity index 93%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/KeyProcessor.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyProcessor.java
index 103856f751..d383c0017c 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/KeyProcessor.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyProcessor.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.processor;
+package io.gravitee.am.identityprovider.api.oidc.jwt;
 
 import com.nimbusds.jwt.proc.JWTProcessor;
 import io.gravitee.am.common.jwt.SignatureAlgorithm;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/resolver/KeyResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyResolver.java
similarity index 93%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/resolver/KeyResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyResolver.java
index c2cebe965d..a2e0221215 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/resolver/KeyResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/oidc/jwt/KeyResolver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.resolver;
+package io.gravitee.am.identityprovider.api.oidc.jwt;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialAuthenticationProvider.java
index c96b79f547..545ce6fe61 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialAuthenticationProvider.java
@@ -22,7 +22,7 @@ import io.gravitee.am.identityprovider.api.common.Request;
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
-public interface SocialAuthenticationProvider extends AuthenticationProvider {
+public interface SocialAuthenticationProvider <T extends SocialIdentityProviderConfiguration> extends AuthenticationProvider {
 
     Request signInUrl(String redirectUri);
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialIdentityProviderConfiguration.java
new file mode 100644
index 0000000000..04d8d4952c
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-api/src/main/java/io/gravitee/am/identityprovider/api/social/SocialIdentityProviderConfiguration.java
@@ -0,0 +1,47 @@
+/**
+ * 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.identityprovider.api.social;
+
+import io.gravitee.am.identityprovider.api.IdentityProviderConfiguration;
+
+import java.util.Set;
+
+/**
+ * @author Eric LELEU (eric.leleu at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public interface SocialIdentityProviderConfiguration extends IdentityProviderConfiguration {
+    public String getClientId();
+
+    public String getClientSecret();
+
+    public Set<String> getScopes();
+
+    public String getUserAuthorizationUri();
+
+    public String getAccessTokenUri();
+
+    public String getUserProfileUri();
+
+    public String getCodeParameter();
+
+    public String getResponseType();
+
+    public Integer getConnectTimeout();
+
+    public Integer getMaxPoolSize();
+
+}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/pom.xml
index 96efce9fbf..aa9f979a28 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/pom.xml
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/pom.xml
@@ -52,6 +52,12 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>io.gravitee.am.identityprovider</groupId>
+            <artifactId>gravitee-am-identityprovider-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <!-- Spring -->
         <dependency>
             <groupId>org.springframework</groupId>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProvider.java
index b3cbf71817..60de051471 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProvider.java
@@ -38,13 +38,4 @@ public class AzureADIdentityProvider implements SocialIdentityProvider {
         return AzureADAuthenticationProvider.class;
     }
 
-    @Override
-    public Class<? extends IdentityProviderMapper> mapper() {
-        return AzureADIdentityProviderMapper.class;
-    }
-
-    @Override
-    public Class<? extends IdentityProviderRoleMapper> roleMapper() {
-        return AzureADIdentityProviderRoleMapper.class;
-    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderConfiguration.java
index 07eb81978a..7a66028b16 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/AzureADIdentityProviderConfiguration.java
@@ -15,17 +15,24 @@
  */
 package io.gravitee.am.identityprovider.azure;
 
-import io.gravitee.am.common.oidc.Scope;
-import io.gravitee.am.identityprovider.api.IdentityProviderConfiguration;
+import io.gravitee.am.common.jwt.SignatureAlgorithm;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectIdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.oidc.jwt.KeyResolver;
 
-import java.util.HashSet;
 import java.util.Set;
 
 /**
  * @author Eric LELEU (eric.leleu at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class AzureADIdentityProviderConfiguration implements IdentityProviderConfiguration {
+public class AzureADIdentityProviderConfiguration implements OpenIDConnectIdentityProviderConfiguration {
+
+    private static final String DEFAULT_RESPONSE_TYPE = "code";
+
+    public static final String HOST_MICROSOFT_LOGIN = "https://login.microsoftonline.com/";
+    public static final String AUTHORIZATION_PATH = "/oauth2/v2.0/authorize";
+    public static final String TOKEN_PATH = "/oauth2/v2.0/token";
+    public static final String JWKS_PATH = "/discovery/v2.0/keys";
 
     public static final String CODE_PARAMETER = "code";
     private String tenantId;
@@ -96,4 +103,49 @@ public class AzureADIdentityProviderConfiguration implements IdentityProviderCon
     public void setMaxPoolSize(Integer maxPoolSize) {
         this.maxPoolSize = maxPoolSize;
     }
+
+    @Override
+    public String getWellKnownUri() {
+        return null;
+    }
+
+    @Override
+    public boolean isUseIdTokenForUserInfo() {
+        return true;
+    }
+
+    @Override
+    public KeyResolver getPublicKeyResolver() {
+        return KeyResolver.JWKS_URL;
+    }
+
+    @Override
+    public SignatureAlgorithm getSignatureAlgorithm() {
+        return SignatureAlgorithm.RS256;
+    }
+
+    @Override
+    public String getResolverParameter() {
+        return HOST_MICROSOFT_LOGIN + getTenantId() + JWKS_PATH;
+    }
+
+    @Override
+    public String getUserAuthorizationUri() {
+        return HOST_MICROSOFT_LOGIN + getTenantId() + AUTHORIZATION_PATH;
+    }
+
+    @Override
+    public String getAccessTokenUri() {
+        return HOST_MICROSOFT_LOGIN + getTenantId() + TOKEN_PATH;
+    }
+
+    @Override
+    public String getUserProfileUri() {
+        return null;
+    }
+
+    @Override
+    public String getResponseType() {
+        return DEFAULT_RESPONSE_TYPE;
+    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProvider.java
index 3a0de65390..150c4cc0bd 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProvider.java
@@ -16,124 +16,72 @@
 package io.gravitee.am.identityprovider.azure.authentication;
 
 import com.nimbusds.jwt.proc.JWTProcessor;
-import io.gravitee.am.common.exception.authentication.BadCredentialsException;
 import io.gravitee.am.common.jwt.SignatureAlgorithm;
-import io.gravitee.am.common.oauth2.Parameters;
-import io.gravitee.am.common.oauth2.TokenTypeHint;
-import io.gravitee.am.common.oidc.*;
-import io.gravitee.am.common.utils.SecureRandomString;
-import io.gravitee.am.common.web.UriBuilder;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.DefaultUser;
-import io.gravitee.am.identityprovider.api.User;
+import io.gravitee.am.common.oidc.Scope;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.api.common.Request;
-import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectIdentityProviderConfiguration;
 import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderMapper;
-import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.azure.authentication.spring.AzureADAuthenticationProviderConfiguration;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.remote.RemoteJWKSourceResolver;
-import io.gravitee.am.identityprovider.azure.jwt.processor.JWKSKeyProcessor;
-import io.gravitee.am.identityprovider.azure.utils.URLEncodedUtils;
-import io.gravitee.am.model.http.BasicNameValuePair;
-import io.gravitee.am.model.http.NameValuePair;
-import io.gravitee.common.http.HttpHeaders;
-import io.gravitee.common.http.HttpMethod;
-import io.gravitee.common.http.MediaType;
-import io.reactivex.Maybe;
-import io.vertx.core.json.JsonObject;
-import io.vertx.reactivex.core.buffer.Buffer;
+import io.gravitee.am.identityprovider.common.oauth2.authentication.AbstractOpenIDConnectAuthenticationProvider;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.remote.RemoteJWKSourceResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.JWKSKeyProcessor;
 import io.vertx.reactivex.ext.web.client.WebClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Import;
 
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
+import java.util.HashSet;
 
 /**
  * @author Eric LELEU (eric.leleu at graviteesource.com)
  * @author GraviteeSource Team
  */
 @Import(AzureADAuthenticationProviderConfiguration.class)
-public class AzureADAuthenticationProvider implements OpenIDConnectAuthenticationProvider, InitializingBean {
-    private static final Logger LOGGER = LoggerFactory.getLogger(AzureADAuthenticationProvider.class);
-
-    private static final String DEFAULT_RESPONSE_TYPE = "code";
-
-    private static final String ID_TOKEN_PARAMETER = "id_token";
-
-    public static final String HOST_MICROSOFT_LOGIN = "https://login.microsoftonline.com/";
-    public static final String AUTHORIZATION_PATH = "/oauth2/v2.0/authorize";
-    public static final String TOKEN_PATH = "/oauth2/v2.0/token";
-    public static final String JWKS_PATH = "/discovery/v2.0/keys";
+public class AzureADAuthenticationProvider extends AbstractOpenIDConnectAuthenticationProvider<AzureADIdentityProviderConfiguration> {
 
     @Autowired
     @Qualifier("azureAdWebClient")
     private WebClient client;
 
     @Autowired
-    private AzureADIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Autowired
-    private AzureADIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Autowired
     private AzureADIdentityProviderConfiguration configuration;
 
-    private JWTProcessor jwtProcessor;
-
-    public String getUserAuthorizationUri() {
-        return HOST_MICROSOFT_LOGIN + configuration.getTenantId() + AUTHORIZATION_PATH;
+    @Override
+    public OpenIDConnectIdentityProviderConfiguration getConfiguration() {
+        return this.configuration;
     }
 
-    public String getJwksUri() {
-        return HOST_MICROSOFT_LOGIN + configuration.getTenantId() + JWKS_PATH;
+    @Override
+    protected WebClient getClient() {
+        return this.client;
     }
 
-    public String getTokenUri() {
-        return HOST_MICROSOFT_LOGIN + configuration.getTenantId() + TOKEN_PATH;
+    public void setJwtProcessor(JWTProcessor jwtProcessor) {
+        this.jwtProcessor = jwtProcessor;
     }
 
-    public String getResponseType() {
-        return DEFAULT_RESPONSE_TYPE;
+    @Override
+    protected IdentityProviderMapper getIdentityProviderMapper() {
+        return this.mapper;
     }
 
-    public void setJwtProcessor(JWTProcessor jwtProcessor) {
-        this.jwtProcessor = jwtProcessor;
+    @Override
+    protected IdentityProviderRoleMapper getIdentityProviderRoleMapper() {
+        return this.roleMapper;
     }
-
     @Override
     public Request signInUrl(String redirectUri) {
-        try {
-            UriBuilder builder = UriBuilder.fromHttpUrl(getUserAuthorizationUri());
-            builder.addParameter(Parameters.CLIENT_ID, configuration.getClientId());
-            builder.addParameter(Parameters.RESPONSE_TYPE, getResponseType());
-
-            forceOpenIdScope();
-
-            // append scopes
-            if (configuration.getScopes() != null && !configuration.getScopes().isEmpty()) {
-                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, configuration.getScopes()));
-            }
-
-            // append redirect_uri
-            builder.addParameter(Parameters.REDIRECT_URI, configuration.isEncodeRedirectUri() ? UriBuilder.encodeURIComponent(redirectUri) : redirectUri);
-
-            Request request = new Request();
-            request.setMethod(HttpMethod.GET);
-            request.setUri(builder.buildString());
-            return request;
-        } catch (Exception e) {
-            LOGGER.error("An error occurs while building OpenID Connect Sign In URL", e);
-            return null;
-        }
+        return super.signInUrl(redirectUri);
     }
 
     private void forceOpenIdScope() {
@@ -141,90 +89,8 @@ public class AzureADAuthenticationProvider implements OpenIDConnectAuthenticatio
             configuration.setScopes(new HashSet<>());
         }
         configuration.getScopes().add(Scope.OPENID.getKey());
-    }
-
-    @Override
-    public AuthenticationFlow authenticationFlow() {
-        return AuthenticationFlow.AUTHORIZATION_CODE_FLOW;
-    }
-
-    @Override
-    public Maybe<User> loadUserByUsername(Authentication authentication) {
-        return authenticate(authentication)
-                .flatMap(token -> profile(token, authentication));
-    }
-
-    @Override
-    public Maybe<User> loadUserByUsername(String username) {
-        return Maybe.empty();
-    }
-
-    private Maybe<Token> authenticate(Authentication authentication) {
-        // authorization code flow, exchange code for an access token
-        // prepare body request parameters
-        final String authorizationCode = authentication.getContext().request().parameters().getFirst(configuration.getCodeParameter());
-        if (authorizationCode == null || authorizationCode.isEmpty()) {
-            LOGGER.debug("Authorization code is missing, skip authentication");
-            return Maybe.error(new BadCredentialsException("Missing authorization code"));
-        }
-
-        List<NameValuePair> urlParameters = new ArrayList<>();
-        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_ID, configuration.getClientId()));
-        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_SECRET, configuration.getClientSecret()));
-        urlParameters.add(new BasicNameValuePair(Parameters.REDIRECT_URI, (String) authentication.getContext().get(Parameters.REDIRECT_URI)));
-        urlParameters.add(new BasicNameValuePair(Parameters.CODE, authorizationCode));
-        urlParameters.add(new BasicNameValuePair(Parameters.GRANT_TYPE, "authorization_code"));
-        String bodyRequest = URLEncodedUtils.format(urlParameters);
-
-        return client.postAbs(getTokenUri())
-                .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(bodyRequest.length()))
-                .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
-                .rxSendBuffer(Buffer.buffer(bodyRequest))
-                .toMaybe()
-                .map(httpResponse -> {
-                    if (httpResponse.statusCode() != 200) {
-                        throw new BadCredentialsException(httpResponse.statusMessage());
-                    }
-
-                    JsonObject response = httpResponse.bodyAsJsonObject();
-                    // put the id_token in context for later use
-                    String idToken = response.getString(ID_TOKEN_PARAMETER);
-                    authentication.getContext().set(ID_TOKEN_PARAMETER, idToken);
-                    return new Token(idToken, TokenTypeHint.ID_TOKEN);
-                });
-
-    }
-
-    private Maybe<User> profile(Token token, Authentication authentication) {
-        return retrieveUserFromIdToken(token.getValue());
-    }
-
-    private Maybe<User> retrieveUserFromIdToken(String idToken) {
-        return Maybe.fromCallable(() -> jwtProcessor.process(idToken, null))
-                .onErrorResumeNext(ex -> {
-                    return Maybe.error(new BadCredentialsException(ex.getMessage()));
-                })
-                .map(jwtClaimsSet -> createUser(jwtClaimsSet.getClaims()));
-    }
-
-    private User createUser(Map<String, Object> attributes) {
-        String username = (String) attributes.getOrDefault(StandardClaims.PREFERRED_USERNAME, attributes.get(StandardClaims.SUB));
-        User user = new DefaultUser(username);
-        ((DefaultUser) user).setId((String) attributes.get(StandardClaims.SUB));
-        // set additional information
-        Map<String, Object> additionalInformation = new HashMap<>();
-        additionalInformation.put(StandardClaims.SUB, attributes.get(StandardClaims.SUB));
-        additionalInformation.put(StandardClaims.PREFERRED_USERNAME, username);
-        // apply user mapping
-        additionalInformation.putAll(applyUserMapping(attributes));
-        // update username if user mapping has been changed
-        if (additionalInformation.containsKey(StandardClaims.PREFERRED_USERNAME)) {
-            ((DefaultUser) user).setUsername((String) additionalInformation.get(StandardClaims.PREFERRED_USERNAME));
-        }
-        ((DefaultUser) user).setAdditionalInformation(additionalInformation);
-        // set user roles
-        ((DefaultUser) user).setRoles(applyRoleMapping(attributes));
-        return user;
+        configuration.getScopes().add(Scope.PROFILE.getKey());
+        configuration.getScopes().add(Scope.EMAIL.getKey());
     }
 
     @Override
@@ -233,89 +99,21 @@ public class AzureADAuthenticationProvider implements OpenIDConnectAuthenticatio
 
         // check configuration
         // a client secret is required if authorization code flow is used
-        if (io.gravitee.am.common.oauth2.ResponseType.CODE.equals(getResponseType())
+        if (io.gravitee.am.common.oauth2.ResponseType.CODE.equals(configuration.getResponseType())
                 && (configuration.getClientSecret() == null || configuration.getClientSecret().isEmpty())) {
             throw new IllegalArgumentException("A client_secret must be supplied in order to use the Authorization Code flow");
         }
 
+        forceOpenIdScope();
+
         // generate jwt processor if we try to fetch user information from the ID Token
         generateJWTProcessor();
     }
 
     private void generateJWTProcessor() {
         final SignatureAlgorithm signature = SignatureAlgorithm.RS256;
-        JWKSKeyProcessor keyProcessor = new JWKSKeyProcessor();
-        keyProcessor.setJwkSourceResolver(new RemoteJWKSourceResolver(getJwksUri()));
+        JWKSKeyProcessor keyProcessor = new JWKSKeyProcessor<>();
+        keyProcessor.setJwkSourceResolver(new RemoteJWKSourceResolver(configuration.getResolverParameter()));
         jwtProcessor = keyProcessor.create(signature);
     }
-
-    private Map<String, Object> applyUserMapping(Map<String, Object> attributes) {
-        if (!mappingEnabled()) {
-            // set default standard claims
-            return Stream.concat(StandardClaims.claims().stream(), CustomClaims.claims().stream())
-                    .filter(claimName -> attributes.containsKey(claimName))
-                    .collect(Collectors.toMap(claimName -> claimName, claimName -> attributes.get(claimName)));
-        }
-
-        Map<String, Object> claims = new HashMap<>();
-        this.mapper.getMappers().forEach((k, v) -> {
-            if (attributes.containsKey(v)) {
-                claims.put(k, attributes.get(v));
-            }
-        });
-        return claims;
-    }
-
-    private List<String> applyRoleMapping(Map<String, Object> attributes) {
-        if (!roleMappingEnabled()) {
-            return Collections.emptyList();
-        }
-
-        Set<String> roles = new HashSet<>();
-        roleMapper.getRoles().forEach((role, users) -> {
-            Arrays.asList(users).forEach(u -> {
-                // role mapping have the following syntax userAttribute=userValue
-                String[] roleMapping = u.split("=",2);
-                String userAttribute = roleMapping[0];
-                String userValue = roleMapping[1];
-                if (attributes.containsKey(userAttribute)) {
-                    Object attribute = attributes.get(userAttribute);
-                    // attribute is a list
-                    if (attribute instanceof Collection && ((Collection) attribute).contains(userValue)) {
-                        roles.add(role);
-                    } else if (userValue.equals(attributes.get(userAttribute))) {
-                        roles.add(role);
-                    }
-                }
-            });
-        });
-
-        return new ArrayList<>(roles);
-    }
-
-    private boolean mappingEnabled() {
-        return this.mapper != null && this.mapper.getMappers() != null && !this.mapper.getMappers().isEmpty();
-    }
-
-    private boolean roleMappingEnabled() {
-        return this.roleMapper != null && this.roleMapper.getRoles() != null && !this.roleMapper.getRoles().isEmpty();
-    }
-
-    private class Token {
-        private String value;
-        private TokenTypeHint typeHint;
-
-        public Token(String value, TokenTypeHint typeHint) {
-            this.value = value;
-            this.typeHint = typeHint;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        public TokenTypeHint getTypeHint() {
-            return typeHint;
-        }
-    }
 }
\ No newline at end of file
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/spring/AzureADAuthenticationProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/spring/AzureADAuthenticationProviderConfiguration.java
index c024be83de..98d891e008 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/spring/AzureADAuthenticationProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/authentication/spring/AzureADAuthenticationProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.azure.authentication.spring;
 
 import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.common.oauth2.utils.WebClientBuilder;
 import io.vertx.ext.web.client.WebClientOptions;
 import io.vertx.reactivex.core.Vertx;
 import io.vertx.reactivex.ext.web.client.WebClient;
@@ -30,9 +31,6 @@ import org.springframework.context.annotation.Configuration;
  */
 @Configuration
 public class AzureADAuthenticationProviderConfiguration {
-
-    private static final String DEFAULT_USER_AGENT = "Gravitee.io-AM/3";
-
     @Autowired
     private Vertx vertx;
 
@@ -42,12 +40,6 @@ public class AzureADAuthenticationProviderConfiguration {
     @Bean
     @Qualifier("azureAdWebClient")
     public WebClient httpClient() {
-        WebClientOptions httpClientOptions = new WebClientOptions();
-        httpClientOptions
-                .setUserAgent(DEFAULT_USER_AGENT)
-                .setConnectTimeout(configuration.getConnectTimeout())
-                .setMaxPoolSize(configuration.getMaxPoolSize());
-
-        return WebClient.create(vertx, httpClientOptions);
+        return WebClientBuilder.build(vertx, configuration);
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/hmac/MACJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/hmac/MACJWKSourceResolver.java
deleted file mode 100644
index 3d5086c94b..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/hmac/MACJWKSourceResolver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * 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.identityprovider.azure.jwt.jwks.hmac;
-
-import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.JWKSet;
-import com.nimbusds.jose.jwk.OctetSequenceKey;
-import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.JWKSourceResolver;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class MACJWKSourceResolver<C extends SecurityContext> implements JWKSourceResolver<C> {
-
-    private final JWK jwk;
-
-    public MACJWKSourceResolver(String secretKey) {
-        this.jwk = new OctetSequenceKey.Builder(secretKey.getBytes()).build();
-    }
-
-    @Override
-    public JWKSource<C> resolve() {
-        return new ImmutableJWKSet<>(new JWKSet(jwk));
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/resources/schemas/schema-form.json b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/resources/schemas/schema-form.json
index a061dee1aa..adaa42e9dc 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/resources/schemas/schema-form.json
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/resources/schemas/schema-form.json
@@ -1,7 +1,7 @@
 {
   "type" : "object",
   "id" : "urn:jsonschema:io:gravitee:am:identityprovider:azure:AzureADIdentityProvider",
-  "description": "Azure AD identity provider, let you register and set up permissions and authorization options for OpenID Connect protocol.\n\nhttps://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app",
+  "description": "Azure AD identity provider, let you register and set up permissions and authorization options for OpenID Connect protocol.\n\nhttps://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app \n\n (openid, email and profile scopes are added by default)",
   "properties" : {
     "tenantId" : {
       "type" : "string",
@@ -25,7 +25,7 @@
     "scopes": {
       "type": "array",
       "title": "Scopes",
-      "description": "Scopes let you specify exactly what type of access you need. Scopes limit access for OAuth tokens.",
+      "description": "Scopes let you specify exactly what type of access you need. Scopes limit access for OAuth tokens. (openid, email and profile scopes are added by default)",
       "items":
       {
         "type": "string"
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-common/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/pom.xml
new file mode 100644
index 0000000000..a45e54c06a
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>gravitee-am-identityprovider</artifactId>
+        <groupId>io.gravitee.am.identityprovider</groupId>
+        <version>3.2.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>gravitee-am-identityprovider-common</artifactId>
+
+    <dependencies>
+        <!-- Gravitee.io -->
+        <dependency>
+            <groupId>io.gravitee.am.identityprovider</groupId>
+            <artifactId>gravitee-am-identityprovider-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.gravitee.am.service</groupId>
+            <artifactId>gravitee-am-service</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Spring -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Vert.x -->
+        <dependency>
+            <groupId>io.vertx</groupId>
+            <artifactId>vertx-rx-java2</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.vertx</groupId>
+            <artifactId>vertx-web-client</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractOpenIDConnectAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractOpenIDConnectAuthenticationProvider.java
new file mode 100644
index 0000000000..8fd8cc67ea
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractOpenIDConnectAuthenticationProvider.java
@@ -0,0 +1,278 @@
+/**
+ * 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.identityprovider.common.oauth2.authentication;
+
+import com.nimbusds.jwt.proc.JWTProcessor;
+import io.gravitee.am.common.exception.authentication.BadCredentialsException;
+import io.gravitee.am.common.jwt.SignatureAlgorithm;
+import io.gravitee.am.common.oauth2.Parameters;
+import io.gravitee.am.common.oauth2.TokenTypeHint;
+import io.gravitee.am.common.oidc.AuthenticationFlow;
+import io.gravitee.am.common.oidc.CustomClaims;
+import io.gravitee.am.common.oidc.ResponseType;
+import io.gravitee.am.common.oidc.StandardClaims;
+import io.gravitee.am.common.utils.SecureRandomString;
+import io.gravitee.am.common.web.UriBuilder;
+import io.gravitee.am.identityprovider.api.Authentication;
+import io.gravitee.am.identityprovider.api.DefaultUser;
+import io.gravitee.am.identityprovider.api.User;
+import io.gravitee.am.identityprovider.api.common.Request;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectIdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.oidc.jwt.KeyResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.hmac.MACJWKSourceResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.remote.RemoteJWKSourceResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.rsa.RSAJWKSourceResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.AbstractKeyProcessor;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.HMACKeyProcessor;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.JWKSKeyProcessor;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.RSAKeyProcessor;
+import io.gravitee.am.identityprovider.common.oauth2.utils.URLEncodedUtils;
+import io.gravitee.am.model.http.BasicNameValuePair;
+import io.gravitee.am.model.http.NameValuePair;
+import io.gravitee.common.http.HttpHeaders;
+import io.gravitee.common.http.HttpMethod;
+import io.gravitee.common.http.MediaType;
+import io.reactivex.Maybe;
+import io.vertx.core.json.JsonObject;
+import io.vertx.reactivex.core.MultiMap;
+import io.vertx.reactivex.core.buffer.Buffer;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
+
+/**
+ * @author Eric LELEU (eric.leleu at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public abstract class AbstractOpenIDConnectAuthenticationProvider < T extends OpenIDConnectIdentityProviderConfiguration> extends AbstractSocialAuthenticationProvider implements OpenIDConnectAuthenticationProvider, InitializingBean {
+
+    public static final String HASH_VALUE_PARAMETER = "urlHash";
+    public static final String ACCESS_TOKEN_PARAMETER = "access_token";
+    public static final String ID_TOKEN_PARAMETER = "id_token";
+
+    protected JWTProcessor jwtProcessor;
+
+    @Override
+    public AuthenticationFlow authenticationFlow() {
+        return io.gravitee.am.common.oauth2.ResponseType.CODE.equals(getConfiguration().getResponseType()) ? AuthenticationFlow.AUTHORIZATION_CODE_FLOW : AuthenticationFlow.IMPLICIT_FLOW;
+    }
+
+    @Override
+    protected Map<String, Object> defaultClaims(Map attributes) {
+        return Stream.concat(StandardClaims.claims().stream(), CustomClaims.claims().stream())
+                .filter(claimName -> attributes.containsKey(claimName))
+                .collect(Collectors.toMap(claimName -> claimName, claimName -> attributes.get(claimName)));
+    }
+
+    protected Maybe<User> retrieveUserFromIdToken(String idToken) {
+        return Maybe.fromCallable(() -> jwtProcessor.process(idToken, null))
+                .onErrorResumeNext(ex -> {
+                    return Maybe.error(new BadCredentialsException(ex.getMessage()));
+                })
+                .map(jwtClaimsSet -> createUser(jwtClaimsSet.getClaims()));
+    }
+
+    @Override
+    public Request signInUrl(String redirectUri) {
+        try {
+            UriBuilder builder = UriBuilder.fromHttpUrl(getConfiguration().getUserAuthorizationUri());
+            builder.addParameter(Parameters.CLIENT_ID, getConfiguration().getClientId());
+            builder.addParameter(Parameters.RESPONSE_TYPE, getConfiguration().getResponseType());
+            // append scopes
+            if (getConfiguration().getScopes() != null && !getConfiguration().getScopes().isEmpty()) {
+                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, getConfiguration().getScopes()));
+            }
+            // nonce parameter is required for implicit/hybrid flow
+            if (!io.gravitee.am.common.oauth2.ResponseType.CODE.equals(getConfiguration().getResponseType())) {
+                builder.addParameter(io.gravitee.am.common.oidc.Parameters.NONCE, SecureRandomString.generate());
+            }
+            // append redirect_uri
+            builder.addParameter(Parameters.REDIRECT_URI, getConfiguration().isEncodeRedirectUri() ? UriBuilder.encodeURIComponent(redirectUri) : redirectUri);
+
+            Request request = new Request();
+            request.setMethod(HttpMethod.GET);
+            request.setUri(builder.buildString());
+            return request;
+        } catch (Exception e) {
+            LOGGER.error("An error occurs while building OpenID Connect Sign In URL", e);
+            return null;
+        }
+    }
+
+    protected Maybe<Token> authenticate(Authentication authentication) {
+        // implicit flow, retrieve the hashValue of the URL (#access_token=....&token_type=...)
+        if (AuthenticationFlow.IMPLICIT_FLOW.equals(authenticationFlow())){
+            final String hashValue = authentication.getContext().request().parameters().getFirst(HASH_VALUE_PARAMETER);
+            Map<String, String> hashValues = getParams(hashValue.substring(1));
+
+            // implicit flow was used with response_type=id_token token, access token is already fetched, continue
+            if (ResponseType.ID_TOKEN_TOKEN.equals(getConfiguration().getResponseType())) {
+                String accessToken = hashValues.get(ACCESS_TOKEN_PARAMETER);
+                // put the id_token in context for later use
+                authentication.getContext().set(ID_TOKEN_PARAMETER, hashValues.get(ID_TOKEN_PARAMETER));
+                return Maybe.just(new Token(accessToken, TokenTypeHint.ACCESS_TOKEN));
+            }
+
+            // implicit flow was used with response_type=id_token, id token is already fetched, continue
+            if (ResponseType.ID_TOKEN.equals(getConfiguration().getResponseType())) {
+                String idToken = hashValues.get(ID_TOKEN_PARAMETER);
+                return Maybe.just(new Token(idToken, TokenTypeHint.ID_TOKEN));
+            }
+        }
+
+        // authorization code flow, exchange code for an access token
+        // prepare body request parameters
+        final String authorizationCode = authentication.getContext().request().parameters().getFirst(getConfiguration().getCodeParameter());
+        if (authorizationCode == null || authorizationCode.isEmpty()) {
+            LOGGER.debug("Authorization code is missing, skip authentication");
+            return Maybe.error(new BadCredentialsException("Missing authorization code"));
+        }
+
+        List<NameValuePair> urlParameters = new ArrayList<>();
+        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_ID, getConfiguration().getClientId()));
+        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_SECRET, getConfiguration().getClientSecret()));
+        urlParameters.add(new BasicNameValuePair(Parameters.REDIRECT_URI, (String) authentication.getContext().get(Parameters.REDIRECT_URI)));
+        urlParameters.add(new BasicNameValuePair(Parameters.CODE, authorizationCode));
+        urlParameters.add(new BasicNameValuePair(Parameters.GRANT_TYPE, "authorization_code"));
+        String bodyRequest = URLEncodedUtils.format(urlParameters);
+
+        return getClient().postAbs(getConfiguration().getAccessTokenUri())
+                .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(bodyRequest.length()))
+                .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
+                .rxSendBuffer(Buffer.buffer(bodyRequest))
+                .toMaybe()
+                .map(httpResponse -> {
+                    if (httpResponse.statusCode() != 200) {
+                        throw new BadCredentialsException(httpResponse.statusMessage());
+                    }
+
+                    JsonObject response = httpResponse.bodyAsJsonObject();
+                    String accessToken = response.getString(ACCESS_TOKEN_PARAMETER);
+                    if (getConfiguration().isUseIdTokenForUserInfo()) {
+                        // put the id_token in context for later use
+                        String idToken = response.getString(ID_TOKEN_PARAMETER);
+                        authentication.getContext().set(ID_TOKEN_PARAMETER, idToken);
+                    }
+                    return new Token(accessToken, TokenTypeHint.ACCESS_TOKEN);
+                });
+
+    }
+
+    protected Maybe<User> profile(Token token, Authentication authentication) {
+        // we only have the id_token, try to decode it and create the end-user
+        if (TokenTypeHint.ID_TOKEN.equals(token.getTypeHint())) {
+            return retrieveUserFromIdToken(token.getValue());
+        }
+
+        // if it's an access token but user ask for id token verification, try to decode it and create the end-user
+        if (TokenTypeHint.ACCESS_TOKEN.equals(token.getTypeHint()) && getConfiguration().isUseIdTokenForUserInfo()) {
+            if (authentication.getContext().get(ID_TOKEN_PARAMETER) != null) {
+                String idToken = (String) authentication.getContext().get(ID_TOKEN_PARAMETER);
+                return retrieveUserFromIdToken(idToken);
+            } else {
+                // no suitable value to retrieve user
+                return Maybe.error(new BadCredentialsException("No suitable value to retrieve user information"));
+            }
+        }
+
+        // retrieve user claims from the UserInfo Endpoint
+        return getClient().getAbs(getConfiguration().getUserProfileUri())
+                .putHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token.getValue())
+                .rxSend()
+                .toMaybe()
+                .map(httpClientResponse -> {
+                    if (httpClientResponse.statusCode() != 200) {
+                        throw new BadCredentialsException(httpClientResponse.statusMessage());
+                    }
+
+                    return createUser(httpClientResponse.bodyAsJsonObject().getMap());
+                });
+    }
+
+    protected User createUser(Map<String, Object> attributes) {
+        String username = (String) attributes.getOrDefault(StandardClaims.PREFERRED_USERNAME, attributes.get(StandardClaims.SUB));
+        User user = new DefaultUser(username);
+        ((DefaultUser) user).setId((String) attributes.get(StandardClaims.SUB));
+        // set additional information
+        Map<String, Object> additionalInformation = new HashMap<>();
+        additionalInformation.put(StandardClaims.SUB, attributes.get(StandardClaims.SUB));
+        additionalInformation.put(StandardClaims.PREFERRED_USERNAME, username);
+        // apply user mapping
+        additionalInformation.putAll(applyUserMapping(attributes));
+        // update username if user mapping has been changed
+        if (additionalInformation.containsKey(StandardClaims.PREFERRED_USERNAME)) {
+            ((DefaultUser) user).setUsername((String) additionalInformation.get(StandardClaims.PREFERRED_USERNAME));
+        }
+        ((DefaultUser) user).setAdditionalInformation(additionalInformation);
+        // set user roles
+        ((DefaultUser) user).setRoles(applyRoleMapping(attributes));
+        return user;
+    }
+
+    protected void generateJWTProcessor(OpenIDConnectIdentityProviderConfiguration configuration) {
+        if (getConfiguration().isUseIdTokenForUserInfo() || ResponseType.ID_TOKEN.equals(getConfiguration().getResponseType())) {
+            if (getConfiguration().getPublicKeyResolver() == null || getConfiguration().getResolverParameter() == null) {
+                throw new IllegalStateException("An public key resolver must be supply to verify the ID Token");
+            }
+
+            final SignatureAlgorithm signature = (getConfiguration().getSignatureAlgorithm() == null) ? SignatureAlgorithm.RS256 : getConfiguration().getSignatureAlgorithm();
+
+            AbstractKeyProcessor keyProcessor = null;
+            // init JWT key source (Remote URL or from configuration file)
+            if (KeyResolver.JWKS_URL.equals(getConfiguration().getPublicKeyResolver())) {
+                keyProcessor = new JWKSKeyProcessor();
+                keyProcessor.setJwkSourceResolver(new RemoteJWKSourceResolver(getConfiguration().getResolverParameter()));
+            } else {
+                // get the corresponding key processor
+                final String resolverParameter = getConfiguration().getResolverParameter();
+                switch (signature) {
+                    case RS256:
+                    case RS384:
+                    case RS512:
+                        keyProcessor = new RSAKeyProcessor();
+                        keyProcessor.setJwkSourceResolver(new RSAJWKSourceResolver(resolverParameter));
+                        break;
+                    case HS256:
+                    case HS384:
+                    case HS512:
+                        keyProcessor = new HMACKeyProcessor();
+                        keyProcessor.setJwkSourceResolver(new MACJWKSourceResolver(resolverParameter));
+                        break;
+                }
+            }
+
+            Assert.notNull(keyProcessor, "A key processor must be set");
+            jwtProcessor = keyProcessor.create(signature);
+        }
+    }
+
+    private Map<String, String> getParams(String query) {
+        Map<String, String> query_pairs = new LinkedHashMap<>();
+        String[] pairs = query.split("&");
+        for (String pair : pairs) {
+            int idx = pair.indexOf("=");
+            query_pairs.put(pair.substring(0, idx), pair.substring(idx + 1));
+        }
+        return query_pairs;
+    }
+
+}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractSocialAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractSocialAuthenticationProvider.java
new file mode 100644
index 0000000000..d798633a34
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/authentication/AbstractSocialAuthenticationProvider.java
@@ -0,0 +1,161 @@
+/**
+ * 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.identityprovider.common.oauth2.authentication;
+
+import io.gravitee.am.common.oauth2.Parameters;
+import io.gravitee.am.common.oauth2.TokenTypeHint;
+import io.gravitee.am.common.web.UriBuilder;
+import io.gravitee.am.identityprovider.api.Authentication;
+import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.User;
+import io.gravitee.am.identityprovider.api.common.Request;
+import io.gravitee.am.identityprovider.api.social.SocialAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
+import io.gravitee.common.http.HttpMethod;
+import io.reactivex.Maybe;
+import io.vertx.reactivex.ext.web.client.WebClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
+
+/**
+ * @author Eric LELEU (eric.leleu at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public abstract class AbstractSocialAuthenticationProvider<T extends SocialIdentityProviderConfiguration> implements SocialAuthenticationProvider {
+
+    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
+
+    protected abstract T getConfiguration();
+    protected abstract IdentityProviderMapper getIdentityProviderMapper();
+    protected abstract IdentityProviderRoleMapper getIdentityProviderRoleMapper();
+    protected abstract WebClient getClient();
+
+    @Override
+    public Request signInUrl(String redirectUri) {
+        try {
+            UriBuilder builder = UriBuilder.fromHttpUrl(getConfiguration().getUserAuthorizationUri());
+            builder.addParameter(Parameters.CLIENT_ID, getConfiguration().getClientId());
+            builder.addParameter(Parameters.REDIRECT_URI, redirectUri);
+            builder.addParameter(Parameters.RESPONSE_TYPE, getConfiguration().getResponseType());
+            if (getConfiguration().getScopes() != null && !getConfiguration().getScopes().isEmpty()) {
+                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, getConfiguration().getScopes()));
+            }
+
+            Request request = new Request();
+            request.setMethod(HttpMethod.GET);
+            request.setUri(builder.build().toString());
+            return request;
+        } catch (Exception e) {
+            LOGGER.error("An error occurs while building Sign In URL", e);
+            return null;
+        }
+    }
+
+    @Override
+    public Maybe<User> loadUserByUsername(Authentication authentication) {
+        return authenticate(authentication)
+                .flatMap(accessToken -> profile(accessToken, authentication));
+    }
+
+    @Override
+    public Maybe<User> loadUserByUsername(String username) {
+        return Maybe.empty();
+    }
+
+
+    protected Map<String, Object> applyUserMapping(Map<String, Object> attributes) {
+        if (!mappingEnabled()) {
+            return defaultClaims(attributes);
+        }
+
+        Map<String, Object> claims = new HashMap<>();
+        this.getIdentityProviderMapper().getMappers().forEach((k, v) -> {
+            if (attributes.containsKey(v)) {
+                claims.put(k, attributes.get(v));
+            }
+        });
+        return claims;
+    }
+
+    protected List<String> applyRoleMapping(Map<String, Object> attributes) {
+        if (!roleMappingEnabled()) {
+            return Collections.emptyList();
+        }
+
+        Set<String> roles = new HashSet<>();
+        getIdentityProviderRoleMapper().getRoles().forEach((role, users) -> {
+            Arrays.asList(users).forEach(u -> {
+                // role mapping have the following syntax userAttribute=userValue
+                String[] roleMapping = u.split("=",2);
+                String userAttribute = roleMapping[0];
+                String userValue = roleMapping[1];
+                if (attributes.containsKey(userAttribute)) {
+                    Object attribute = attributes.get(userAttribute);
+                    // attribute is a list
+                    if (attribute instanceof Collection && ((Collection) attribute).contains(userValue)) {
+                        roles.add(role);
+                    } else if (userValue.equals(attributes.get(userAttribute))) {
+                        roles.add(role);
+                    }
+                }
+            });
+        });
+
+        return new ArrayList<>(roles);
+    }
+
+    protected abstract Maybe<Token> authenticate(Authentication authentication);
+
+    protected abstract Maybe<User> profile(Token token, Authentication authentication);
+
+    protected abstract Map<String, Object> defaultClaims(Map<String, Object> attributes);
+
+    private boolean mappingEnabled() {
+        return this.getIdentityProviderMapper() != null
+                && this.getIdentityProviderMapper().getMappers() != null
+                && !this.getIdentityProviderMapper().getMappers().isEmpty();
+    }
+
+    private boolean roleMappingEnabled() {
+        return this.getIdentityProviderRoleMapper() != null
+                && this.getIdentityProviderRoleMapper().getRoles() != null
+                && !this.getIdentityProviderRoleMapper().getRoles().isEmpty();
+    }
+
+
+    protected final class Token {
+        private String value;
+        private TokenTypeHint typeHint;
+
+        public Token(String value, TokenTypeHint typeHint) {
+            this.value = value;
+            this.typeHint = typeHint;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public TokenTypeHint getTypeHint() {
+            return typeHint;
+        }
+    }
+}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java
similarity index 90%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java
index 3814228207..6d564283cd 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/hmac/MACJWKSourceResolver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.oauth2.jwt.jwks.hmac;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.hmac;
 
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.JWKSet;
@@ -21,7 +21,7 @@ import com.nimbusds.jose.jwk.OctetSequenceKey;
 import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.JWKSourceResolver;
+import io.gravitee.am.identityprovider.api.oidc.jwt.JWKSourceResolver;
 
 /**
  * @author David BRASSELY (david.brassely at graviteesource.com)
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/remote/RemoteJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java
similarity index 90%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/remote/RemoteJWKSourceResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java
index 940c1177ed..018cd34217 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/remote/RemoteJWKSourceResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.jwks.remote;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.remote;
 
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.jwk.source.RemoteJWKSet;
 import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.JWKSourceResolver;
+import io.gravitee.am.identityprovider.api.oidc.jwt.JWKSourceResolver;
 
 import java.net.URL;
 
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/rsa/RSAJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java
similarity index 95%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/rsa/RSAJWKSourceResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java
index 9a1ea34a6d..2c9c542619 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/jwks/rsa/RSAJWKSourceResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.jwks.rsa;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.rsa;
 
 import com.nimbusds.jose.jwk.JWK;
 import com.nimbusds.jose.jwk.JWKSet;
@@ -25,7 +25,7 @@ import com.nimbusds.jose.proc.SecurityContext;
 import com.nimbusds.jose.util.Base64;
 import com.nimbusds.jose.util.Base64URL;
 import com.nimbusds.jose.util.X509CertUtils;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.JWKSourceResolver;
+import io.gravitee.am.identityprovider.api.oidc.jwt.JWKSourceResolver;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/AbstractKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/AbstractKeyProcessor.java
similarity index 89%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/AbstractKeyProcessor.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/AbstractKeyProcessor.java
index d6faa4ce56..6395d4a3d9 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/AbstractKeyProcessor.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/AbstractKeyProcessor.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.processor;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.processor;
 
 import com.nimbusds.jose.jwk.source.JWKSource;
 import com.nimbusds.jose.proc.JWSKeySelector;
@@ -22,7 +22,8 @@ import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
 import com.nimbusds.jwt.proc.DefaultJWTProcessor;
 import com.nimbusds.jwt.proc.JWTProcessor;
 import io.gravitee.am.common.jwt.SignatureAlgorithm;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.JWKSourceResolver;
+import io.gravitee.am.identityprovider.api.oidc.jwt.JWKSourceResolver;
+import io.gravitee.am.identityprovider.api.oidc.jwt.KeyProcessor;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/HMACKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/HMACKeyProcessor.java
similarity index 97%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/HMACKeyProcessor.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/HMACKeyProcessor.java
index e801fa6eb3..47d2112da7 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/HMACKeyProcessor.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/HMACKeyProcessor.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.processor;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.processor;
 
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/JWKSKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/JWKSKeyProcessor.java
similarity index 95%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/JWKSKeyProcessor.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/JWKSKeyProcessor.java
index 19090249d3..2378508bb5 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/JWKSKeyProcessor.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/JWKSKeyProcessor.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.processor;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.processor;
 
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.jwk.source.JWKSource;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/RSAKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/RSAKeyProcessor.java
similarity index 97%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/RSAKeyProcessor.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/RSAKeyProcessor.java
index 3ff9f31667..6f8007cfa0 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/jwt/processor/RSAKeyProcessor.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/jwt/processor/RSAKeyProcessor.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.jwt.processor;
+package io.gravitee.am.identityprovider.common.oauth2.jwt.processor;
 
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/resolver/KeyResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/resolver/KeyResolver.java
similarity index 92%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/resolver/KeyResolver.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/resolver/KeyResolver.java
index c679507f12..7ef12dc089 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/resolver/KeyResolver.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/resolver/KeyResolver.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.oauth2.resolver;
+package io.gravitee.am.identityprovider.common.oauth2.resolver;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/utils/URLEncodedUtils.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/URLEncodedUtils.java
similarity index 97%
rename from gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/utils/URLEncodedUtils.java
rename to gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/URLEncodedUtils.java
index aa9c8b7a0b..90ba7b7cc5 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/main/java/io/gravitee/am/identityprovider/azure/utils/URLEncodedUtils.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/URLEncodedUtils.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.gravitee.am.identityprovider.azure.utils;
+package io.gravitee.am.identityprovider.common.oauth2.utils;
 
 import io.gravitee.am.model.http.NameValuePair;
 
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/WebClientBuilder.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/WebClientBuilder.java
new file mode 100644
index 0000000000..e7a0e6d3e1
--- /dev/null
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-common/src/main/java/io/gravitee/am/identityprovider/common/oauth2/utils/WebClientBuilder.java
@@ -0,0 +1,40 @@
+/**
+ * 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.identityprovider.common.oauth2.utils;
+
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
+import io.vertx.ext.web.client.WebClientOptions;
+import io.vertx.reactivex.core.Vertx;
+import io.vertx.reactivex.ext.web.client.WebClient;
+
+/**
+ * @author Eric LELEU (eric.leleu at graviteesource.com)
+ * @author GraviteeSource Team
+ */
+public class WebClientBuilder {
+
+    private static final String DEFAULT_USER_AGENT = "Gravitee.io-AM/3";
+
+    public static WebClient build(Vertx vertx, SocialIdentityProviderConfiguration configuration) {
+        WebClientOptions httpClientOptions = new WebClientOptions();
+        httpClientOptions
+                .setUserAgent(DEFAULT_USER_AGENT)
+                .setConnectTimeout(configuration.getConnectTimeout())
+                .setMaxPoolSize(configuration.getMaxPoolSize());
+
+        return WebClient.create(vertx, httpClientOptions);
+    }
+}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/pom.xml
index 203b3effb6..2b90628ae9 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/pom.xml
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/pom.xml
@@ -33,7 +33,7 @@
         <!-- Gravitee.io -->
         <dependency>
             <groupId>io.gravitee.am.identityprovider</groupId>
-            <artifactId>gravitee-am-identityprovider-api</artifactId>
+            <artifactId>gravitee-am-identityprovider-common</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProvider.java
index 7485f3f8e0..74726a4857 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProvider.java
@@ -38,13 +38,4 @@ public class FacebookIdentityProvider implements SocialIdentityProvider {
         return FacebookAuthenticationProvider.class;
     }
 
-    @Override
-    public Class<? extends IdentityProviderMapper> mapper() {
-        return FacebookIdentityProviderMapper.class;
-    }
-
-    @Override
-    public Class<? extends IdentityProviderRoleMapper> roleMapper() {
-        return FacebookIdentityProviderRoleMapper.class;
-    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderConfiguration.java
index fca1ecde62..ff9767ae6e 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.facebook;
 
 import io.gravitee.am.identityprovider.api.IdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
 
 import java.util.Set;
 
@@ -23,7 +24,7 @@ import java.util.Set;
  * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class FacebookIdentityProviderConfiguration implements IdentityProviderConfiguration {
+public class FacebookIdentityProviderConfiguration implements SocialIdentityProviderConfiguration {
 
     private static final String USER_AUTHORIZATION_URI = "https://www.facebook.com/v8.0/dialog/oauth";
     private static final String ACCESS_TOKEN_URI = "https://graph.facebook.com/v8.0/oauth/access_token";
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderMapper.java
deleted file mode 100644
index 8685994a4a..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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.identityprovider.facebook;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
-
-import java.util.Map;
-
-/**
- * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class FacebookIdentityProviderMapper implements IdentityProviderMapper {
-
-    private Map<String, String> mappers;
-
-    public Map<String, String> getMappers() {
-        return this.mappers;
-    }
-
-    public void setMappers(Map<String, String> mappers) {
-        this.mappers = mappers;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderRoleMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderRoleMapper.java
deleted file mode 100644
index ef63584358..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/FacebookIdentityProviderRoleMapper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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.identityprovider.facebook;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
-
-import java.util.Map;
-
-/**
- * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class FacebookIdentityProviderRoleMapper implements IdentityProviderRoleMapper {
-
-    private Map<String, String[]> roles;
-
-    public Map<String, String[]> getRoles() {
-        return this.roles;
-    }
-
-    public void setRoles(Map<String, String[]> roles) {
-        this.roles = roles;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProvider.java
index 3d7f142ed0..8efd312429 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/main/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProvider.java
@@ -16,20 +16,13 @@
 package io.gravitee.am.identityprovider.facebook.authentication;
 
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
-import io.gravitee.am.common.oauth2.Parameters;
+import io.gravitee.am.common.oauth2.TokenTypeHint;
 import io.gravitee.am.common.oidc.StandardClaims;
-import io.gravitee.am.common.web.UriBuilder;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.DefaultUser;
-import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.identityprovider.api.common.Request;
-import io.gravitee.am.identityprovider.api.social.SocialAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.*;
+import io.gravitee.am.identityprovider.common.oauth2.authentication.AbstractSocialAuthenticationProvider;
 import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderMapper;
-import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.facebook.authentication.spring.FacebookAuthenticationProviderConfiguration;
 import io.gravitee.am.identityprovider.facebook.model.FacebookUser;
-import io.gravitee.common.http.HttpMethod;
 import io.reactivex.Maybe;
 import io.vertx.core.json.JsonArray;
 import io.vertx.core.json.JsonObject;
@@ -41,9 +34,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Import;
 
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
 
-import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
 import static io.gravitee.am.identityprovider.facebook.model.FacebookUser.*;
 
 /**
@@ -51,7 +44,7 @@ import static io.gravitee.am.identityprovider.facebook.model.FacebookUser.*;
  * @author GraviteeSource Team
  */
 @Import(FacebookAuthenticationProviderConfiguration.class)
-public class FacebookAuthenticationProvider implements SocialAuthenticationProvider {
+public class FacebookAuthenticationProvider extends AbstractSocialAuthenticationProvider<FacebookIdentityProviderConfiguration> {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(FacebookAuthenticationProvider.class);
     private static final String CLIENT_ID = "client_id";
@@ -69,44 +62,38 @@ public class FacebookAuthenticationProvider implements SocialAuthenticationProvi
     private FacebookIdentityProviderConfiguration configuration;
 
     @Autowired
-    private FacebookIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Autowired
-    private FacebookIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Override
-    public Request signInUrl(String redirectUri) {
-        try {
-            UriBuilder builder = UriBuilder.fromHttpUrl(configuration.getUserAuthorizationUri());
-            builder.addParameter(Parameters.CLIENT_ID, configuration.getClientId());
-            builder.addParameter(Parameters.REDIRECT_URI, redirectUri);
-            builder.addParameter(Parameters.RESPONSE_TYPE, configuration.getResponseType());
-            if (configuration.getScopes() != null && !configuration.getScopes().isEmpty()) {
-                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, configuration.getScopes()));
-            }
+    protected FacebookIdentityProviderConfiguration getConfiguration() {
+        return this.configuration;
+    }
 
-            Request request = new Request();
-            request.setMethod(HttpMethod.GET);
-            request.setUri(builder.build().toString());
-            return request;
-        } catch (Exception e) {
-            LOGGER.error("An error occurs while building Facebook Sign In URL", e);
-            return null;
-        }
+    @Override
+    protected IdentityProviderMapper getIdentityProviderMapper() {
+        return this.mapper;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(Authentication authentication) {
-        return authenticate(authentication)
-                .flatMap(this::profile);
+    protected IdentityProviderRoleMapper getIdentityProviderRoleMapper() {
+        return this.roleMapper;
+    }
+
+    @Override
+    protected WebClient getClient() {
+        return this.client;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(String username) {
-        return Maybe.empty();
+    public Maybe<User> loadUserByUsername(Authentication authentication) {
+        return authenticate(authentication)
+                .flatMap(token -> this.profile(token, authentication));
     }
 
-    private Maybe<String> authenticate(Authentication authentication) {
+    protected Maybe<Token> authenticate(Authentication authentication) {
 
         // Prepare body request parameters.
         final String authorizationCode = authentication.getContext().request().parameters().getFirst(configuration.getCodeParameter());
@@ -130,15 +117,15 @@ public class FacebookAuthenticationProvider implements SocialAuthenticationProvi
                         return Maybe.error(new BadCredentialsException(httpResponse.bodyAsString()));
                     }
 
-                    return Maybe.just(httpResponse.bodyAsJsonObject().getString(ACCESS_TOKEN));
+                    return Maybe.just(new Token(httpResponse.bodyAsJsonObject().getString(ACCESS_TOKEN), TokenTypeHint.ACCESS_TOKEN));
                 });
     }
 
-    private Maybe<User> profile(String accessToken) {
+    protected Maybe<User> profile(Token accessToken, Authentication auth) {
 
         return client.postAbs(configuration.getUserProfileUri())
                 .rxSendForm(MultiMap.caseInsensitiveMultiMap()
-                        .set(ACCESS_TOKEN, accessToken)
+                        .set(ACCESS_TOKEN, accessToken.getValue())
                         .set(FIELDS, ALL_FIELDS_PARAM))
                 .toMaybe()
                 .flatMap(httpResponse -> {
@@ -166,7 +153,7 @@ public class FacebookAuthenticationProvider implements SocialAuthenticationProvi
         convertClaim(FacebookUser.ID, facebookUser, StandardClaims.PREFERRED_USERNAME, additionalInformation);
 
         // Apply user mapping.
-        additionalInformation.putAll(applyUserMapping(facebookUser));
+        additionalInformation.putAll(applyUserMapping(facebookUser.getMap()));
 
         // Update username if user mapping has changed.
         if (additionalInformation.containsKey(StandardClaims.PREFERRED_USERNAME)) {
@@ -176,59 +163,13 @@ public class FacebookAuthenticationProvider implements SocialAuthenticationProvi
         user.setAdditionalInformation(additionalInformation);
 
         // Set user roles.
-        user.setRoles(applyRoleMapping(facebookUser));
+        user.setRoles(applyRoleMapping(facebookUser.getMap()));
 
         return user;
     }
 
-    /**
-     * TODO: Should be mutualized across idps.
-     */
-    private Map<String, Object> applyUserMapping(JsonObject attributes) {
-        if (!mappingEnabled()) {
-            return defaultClaims(attributes);
-        }
-
-        Map<String, Object> claims = new HashMap<>();
-        this.mapper.getMappers().forEach((k, v) -> {
-            if (attributes.containsKey(v)) {
-                claims.put(k, attributes.getValue(v));
-            }
-        });
-        return claims;
-    }
-
-    /**
-     * TODO: Should be mutualized across idps.
-     */
-    private List<String> applyRoleMapping(JsonObject attributes) {
-        if (!roleMappingEnabled()) {
-            return Collections.emptyList();
-        }
-
-        Set<String> roles = new HashSet<>();
-        roleMapper.getRoles().forEach((role, users) -> {
-            Arrays.asList(users).forEach(u -> {
-                // role mapping have the following syntax userAttribute=userValue
-                String[] roleMapping = u.split("=", 2);
-                String userAttribute = roleMapping[0];
-                String userValue = roleMapping[1];
-                if (attributes.containsKey(userAttribute)) {
-                    Object attribute = attributes.getValue(userAttribute);
-                    // attribute is a list
-                    if (attribute instanceof Collection && ((Collection) attribute).contains(userValue)) {
-                        roles.add(role);
-                    } else if (userValue.equals(attributes.getValue(userAttribute))) {
-                        roles.add(role);
-                    }
-                }
-            });
-        });
-
-        return new ArrayList<>(roles);
-    }
-
-    private Map<String, Object> defaultClaims(JsonObject attributes) {
+    protected Map<String, Object> defaultClaims(Map<String, Object> _attributes) {
+        JsonObject attributes = JsonObject.mapFrom(_attributes);
         Map<String, Object> claims = new HashMap<>();
 
         convertClaim(FacebookUser.ID, attributes, StandardClaims.SUB, claims);
@@ -260,20 +201,6 @@ public class FacebookAuthenticationProvider implements SocialAuthenticationProvi
         return claims;
     }
 
-    /**
-     * TODO: Should be mutualized across idps.
-     */
-    private boolean mappingEnabled() {
-        return this.mapper != null && this.mapper.getMappers() != null && !this.mapper.getMappers().isEmpty();
-    }
-
-    /**
-     * TODO: Should be mutualized across idps.
-     */
-    private boolean roleMappingEnabled() {
-        return this.roleMapper != null && this.roleMapper.getRoles() != null && !this.roleMapper.getRoles().isEmpty();
-    }
-
     private void convertClaim(String sourceName, JsonObject source, String destName, Map<String, Object> dest) {
 
         if (dest.containsKey(destName)) {
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/pom.xml
index 3d094b79a7..4a223f73ca 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/pom.xml
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/pom.xml
@@ -35,7 +35,7 @@
         <!-- Gravitee.io -->
         <dependency>
             <groupId>io.gravitee.am.identityprovider</groupId>
-            <artifactId>gravitee-am-identityprovider-api</artifactId>
+            <artifactId>gravitee-am-identityprovider-common</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProvider.java
index ad48f51e87..990d164ce6 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProvider.java
@@ -37,14 +37,4 @@ public class GithubIdentityProvider implements SocialIdentityProvider {
     public Class<? extends AuthenticationProvider> authenticationProvider() {
         return GithubAuthenticationProvider.class;
     }
-
-    @Override
-    public Class<? extends IdentityProviderMapper> mapper() {
-        return GithubIdentityProviderMapper.class;
-    }
-
-    @Override
-    public Class<? extends IdentityProviderRoleMapper> roleMapper() {
-        return GithubIdentityProviderRoleMapper.class;
-    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderConfiguration.java
index 0b823c78a9..d46bdd23d7 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderConfiguration.java
@@ -16,6 +16,7 @@
 package io.gravitee.am.identityprovider.github;
 
 import io.gravitee.am.identityprovider.api.IdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
 
 import java.util.Set;
 
@@ -23,7 +24,7 @@ import java.util.Set;
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class GithubIdentityProviderConfiguration implements IdentityProviderConfiguration {
+public class GithubIdentityProviderConfiguration implements SocialIdentityProviderConfiguration {
 
     private String USER_AUTHORIZATION_URI = "https://github.com/login/oauth/authorize";
     private String ACCESS_TOKEN_URI = "https://github.com/login/oauth/access_token";
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderMapper.java
deleted file mode 100644
index 23c738fe4d..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderMapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * 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.identityprovider.github;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
-
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class GithubIdentityProviderMapper implements IdentityProviderMapper {
-    private Map<String, String> mappers;
-
-    public Map<String, String> getMappers() {
-        return this.mappers;
-    }
-
-    public void setMappers(Map<String, String> mappers) {
-        this.mappers = mappers;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderRoleMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderRoleMapper.java
deleted file mode 100644
index 2894647502..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/GithubIdentityProviderRoleMapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * 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.identityprovider.github;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
-
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class GithubIdentityProviderRoleMapper implements IdentityProviderRoleMapper {
-    private Map<String, String[]> roles;
-
-    public Map<String, String[]> getRoles() {
-        return this.roles;
-    }
-
-    public void setRoles(Map<String, String[]> roles) {
-        this.roles = roles;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProvider.java
index 4ce84702cc..679ebbbb34 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProvider.java
@@ -16,24 +16,17 @@
 package io.gravitee.am.identityprovider.github.authentication;
 
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
-import io.gravitee.am.common.oauth2.Parameters;
+import io.gravitee.am.common.oauth2.TokenTypeHint;
 import io.gravitee.am.common.oidc.StandardClaims;
-import io.gravitee.am.common.web.UriBuilder;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.DefaultUser;
-import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.identityprovider.api.common.Request;
-import io.gravitee.am.identityprovider.api.social.SocialAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.*;
+import io.gravitee.am.identityprovider.common.oauth2.authentication.AbstractSocialAuthenticationProvider;
+import io.gravitee.am.identityprovider.common.oauth2.utils.URLEncodedUtils;
 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.identityprovider.github.authentication.spring.GithubAuthenticationProviderConfiguration;
 import io.gravitee.am.identityprovider.github.model.GithubUser;
-import io.gravitee.am.identityprovider.github.utils.URLEncodedUtils;
 import io.gravitee.am.model.http.BasicNameValuePair;
 import io.gravitee.am.model.http.NameValuePair;
 import io.gravitee.common.http.HttpHeaders;
-import io.gravitee.common.http.HttpMethod;
 import io.gravitee.common.http.MediaType;
 import io.reactivex.Maybe;
 import io.vertx.reactivex.core.buffer.Buffer;
@@ -44,16 +37,17 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Import;
 
-import java.util.*;
-
-import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
 @Import(GithubAuthenticationProviderConfiguration.class)
-public class GithubAuthenticationProvider implements SocialAuthenticationProvider {
+public class GithubAuthenticationProvider extends AbstractSocialAuthenticationProvider<GithubIdentityProviderConfiguration> {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(GithubAuthenticationProvider.class);
     private static final String CLIENT_ID = "client_id";
@@ -69,44 +63,33 @@ public class GithubAuthenticationProvider implements SocialAuthenticationProvide
     private GithubIdentityProviderConfiguration configuration;
 
     @Autowired
-    private GithubIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Autowired
-    private GithubIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Override
-    public Request signInUrl(String redirectUri) {
-        try {
-            UriBuilder builder = UriBuilder.fromHttpUrl(configuration.getUserAuthorizationUri());
-            builder.addParameter(Parameters.CLIENT_ID, configuration.getClientId());
-            builder.addParameter(Parameters.REDIRECT_URI, redirectUri);
-            builder.addParameter(Parameters.RESPONSE_TYPE, configuration.getResponseType());
-            if (configuration.getScopes() != null && !configuration.getScopes().isEmpty()) {
-                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, configuration.getScopes()));
-            }
+    protected GithubIdentityProviderConfiguration getConfiguration() {
+        return this.configuration;
+    }
 
-            Request request = new Request();
-            request.setMethod(HttpMethod.GET);
-            request.setUri(builder.build().toString());
-            return request;
-        } catch (Exception e) {
-            LOGGER.error("An error occurs while building GitHub Sign In URL", e);
-            return null;
-        }
+    @Override
+    protected IdentityProviderMapper getIdentityProviderMapper() {
+        return this.mapper;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(Authentication authentication) {
-        return authenticate(authentication)
-                .flatMap(accessToken -> profile(accessToken));
+    protected IdentityProviderRoleMapper getIdentityProviderRoleMapper() {
+        return this.roleMapper;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(String username) {
-        return Maybe.empty();
+    protected WebClient getClient() {
+        return this.client;
     }
 
-    private Maybe<String> authenticate(Authentication authentication) {
+    @Override
+    protected Maybe<Token> authenticate(Authentication authentication) {
         // prepare body request parameters
         final String authorizationCode = authentication.getContext().request().parameters().getFirst(configuration.getCodeParameter());
         if (authorizationCode == null || authorizationCode.isEmpty()) {
@@ -131,13 +114,14 @@ public class GithubAuthenticationProvider implements SocialAuthenticationProvide
                     }
 
                     Map<String, String> bodyResponse = URLEncodedUtils.format(httpResponse.bodyAsString());
-                    return bodyResponse.get("access_token");
+                    return new Token(bodyResponse.get("access_token"), TokenTypeHint.ACCESS_TOKEN);
                 });
     }
 
-    private Maybe<User> profile(String accessToken) {
+    @Override
+    protected Maybe<User> profile(Token accessToken, Authentication authentication) {
         return client.getAbs(configuration.getUserProfileUri())
-                .putHeader(HttpHeaders.AUTHORIZATION, "token " + accessToken)
+                .putHeader(HttpHeaders.AUTHORIZATION, "token " + accessToken.getValue())
                 .rxSend()
                 .toMaybe()
                 .map(httpClientResponse -> {
@@ -170,48 +154,7 @@ public class GithubAuthenticationProvider implements SocialAuthenticationProvide
         return user;
     }
 
-    private Map<String, Object> applyUserMapping(Map<String, Object> attributes) {
-        if (!mappingEnabled()) {
-            return defaultClaims(attributes);
-        }
-
-        Map<String, Object> claims = new HashMap<>();
-        this.mapper.getMappers().forEach((k, v) -> {
-            if (attributes.containsKey(v)) {
-                claims.put(k, attributes.get(v));
-            }
-        });
-        return claims;
-    }
-
-    private List<String> applyRoleMapping(Map<String, Object> attributes) {
-        if (!roleMappingEnabled()) {
-            return Collections.emptyList();
-        }
-
-        Set<String> roles = new HashSet<>();
-        roleMapper.getRoles().forEach((role, users) -> {
-            Arrays.asList(users).forEach(u -> {
-                // role mapping have the following syntax userAttribute=userValue
-                String[] roleMapping = u.split("=",2);
-                String userAttribute = roleMapping[0];
-                String userValue = roleMapping[1];
-                if (attributes.containsKey(userAttribute)) {
-                    Object attribute = attributes.get(userAttribute);
-                    // attribute is a list
-                    if (attribute instanceof Collection && ((Collection) attribute).contains(userValue)) {
-                        roles.add(role);
-                    } else if (userValue.equals(attributes.get(userAttribute))) {
-                        roles.add(role);
-                    }
-                }
-            });
-        });
-
-        return new ArrayList<>(roles);
-    }
-
-    private Map<String, Object> defaultClaims(Map<String, Object> attributes) {
+    protected Map<String, Object> defaultClaims(Map<String, Object> attributes) {
         Map<String, Object> claims = new HashMap<>();
         try {
             String[] fullName = String.valueOf(attributes.get(GithubUser.NAME)).split("\\s+");
@@ -260,12 +203,4 @@ public class GithubAuthenticationProvider implements SocialAuthenticationProvide
 
         return claims;
     }
-
-    private boolean mappingEnabled() {
-        return this.mapper != null && this.mapper.getMappers() != null && !this.mapper.getMappers().isEmpty();
-    }
-
-    private boolean roleMappingEnabled() {
-        return this.roleMapper != null && this.roleMapper.getRoles() != null && !this.roleMapper.getRoles().isEmpty();
-    }
 }
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..b3e025da3f 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
@@ -15,8 +15,8 @@
  */
 package io.gravitee.am.identityprovider.github.authentication.spring;
 
+import io.gravitee.am.identityprovider.common.oauth2.utils.WebClientBuilder;
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderConfiguration;
-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.Autowired;
@@ -31,8 +31,6 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class GithubAuthenticationProviderConfiguration {
 
-    private static final String DEFAULT_USER_AGENT = "Gravitee.io-AM/3";
-
     @Autowired
     private Vertx vertx;
 
@@ -42,12 +40,6 @@ public class GithubAuthenticationProviderConfiguration {
     @Bean
     @Qualifier("gitHubWebClient")
     public WebClient httpClient() {
-        WebClientOptions httpClientOptions = new WebClientOptions();
-        httpClientOptions
-                .setUserAgent(DEFAULT_USER_AGENT)
-                .setConnectTimeout(configuration.getConnectTimeout())
-                .setMaxPoolSize(configuration.getMaxPoolSize());
-
-        return WebClient.create(vertx, httpClientOptions);
+        return WebClientBuilder.build(vertx, configuration);
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/utils/URLEncodedUtils.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/utils/URLEncodedUtils.java
deleted file mode 100644
index 33b2e2c699..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/main/java/io/gravitee/am/identityprovider/github/utils/URLEncodedUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * 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.identityprovider.github.utils;
-
-import io.gravitee.am.model.http.NameValuePair;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class URLEncodedUtils {
-
-    public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
-    private static final char QP_SEP_A = '&';
-    private static final String NAME_VALUE_SEPARATOR = "=";
-
-    public static String format(final Iterable<? extends NameValuePair> parameters) {
-        final StringBuilder result = new StringBuilder();
-        for (final NameValuePair parameter : parameters) {
-            final String parameterName = parameter.getName();
-            final String parameterValue = parameter.getValue();
-            if (result.length() > 0) {
-                result.append(QP_SEP_A);
-            }
-            result.append(parameterName);
-            if (parameterValue != null) {
-                result.append(NAME_VALUE_SEPARATOR);
-                result.append(parameterValue);
-            }
-        }
-        return result.toString();
-    }
-
-    public static Map<String, String> format(String query) {
-        Map<String, String> queryPairs = new LinkedHashMap<String, String>();
-        String[] pairs = query.split("&");
-        for (String pair : pairs) {
-            int idx = pair.indexOf("=");
-            queryPairs.put(pair.substring(0, idx), pair.substring(idx + 1));
-        }
-        return queryPairs;
-    }
-
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/pom.xml b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/pom.xml
index 1536488f69..db074d3851 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/pom.xml
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/pom.xml
@@ -52,6 +52,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>io.gravitee.am.identityprovider</groupId>
+            <artifactId>gravitee-am-identityprovider-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- Spring -->
         <dependency>
             <groupId>org.springframework</groupId>
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProvider.java
index 78c89cf20a..e846292430 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProvider.java
@@ -37,14 +37,4 @@ public class OAuth2GenericIdentityProvider implements SocialIdentityProvider {
     public Class<? extends AuthenticationProvider> authenticationProvider() {
         return OAuth2GenericAuthenticationProvider.class;
     }
-
-    @Override
-    public Class<? extends IdentityProviderMapper> mapper() {
-        return OAuth2GenericIdentityProviderMapper.class;
-    }
-
-    @Override
-    public Class<? extends IdentityProviderRoleMapper> roleMapper() {
-        return OAuth2GenericIdentityProviderRoleMapper.class;
-    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderConfiguration.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderConfiguration.java
index 58556c11ad..8d7313b428 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderConfiguration.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderConfiguration.java
@@ -15,9 +15,12 @@
  */
 package io.gravitee.am.identityprovider.oauth2;
 
+import io.gravitee.am.common.jwt.SignatureAlgorithm;
 import io.gravitee.am.identityprovider.api.IdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectIdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.api.oidc.jwt.KeyResolver;
+import io.gravitee.am.identityprovider.api.social.SocialIdentityProviderConfiguration;
 import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-import io.gravitee.am.identityprovider.oauth2.resolver.KeyResolver;
 
 import java.util.Set;
 
@@ -25,7 +28,7 @@ import java.util.Set;
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
-public class OAuth2GenericIdentityProviderConfiguration implements IdentityProviderConfiguration {
+public class OAuth2GenericIdentityProviderConfiguration implements OpenIDConnectIdentityProviderConfiguration {
 
     private static final String CODE_PARAMETER = "code";
     private String clientId;
@@ -167,4 +170,9 @@ public class OAuth2GenericIdentityProviderConfiguration implements IdentityProvi
     public void setMaxPoolSize(Integer maxPoolSize) {
         this.maxPoolSize = maxPoolSize;
     }
+
+    @Override
+    public SignatureAlgorithm getSignatureAlgorithm() {
+        return this.signature == null ? null : this.signature.getAlg();
+    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderMapper.java
deleted file mode 100644
index 942878535e..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderMapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * 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.identityprovider.oauth2;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
-
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class OAuth2GenericIdentityProviderMapper implements IdentityProviderMapper {
-    private Map<String, String> mappers;
-
-    public Map<String, String> getMappers() {
-        return this.mappers;
-    }
-
-    public void setMappers(Map<String, String> mappers) {
-        this.mappers = mappers;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderRoleMapper.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderRoleMapper.java
deleted file mode 100644
index 49aa061ab6..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/OAuth2GenericIdentityProviderRoleMapper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * 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.identityprovider.oauth2;
-
-import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
-
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class OAuth2GenericIdentityProviderRoleMapper implements IdentityProviderRoleMapper {
-    private Map<String, String[]> roles;
-
-    public Map<String, String[]> getRoles() {
-        return this.roles;
-    }
-
-    public void setRoles(Map<String, String[]> roles) {
-        this.roles = roles;
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProvider.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProvider.java
index b80ab0b51d..6a4730ff21 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProvider.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProvider.java
@@ -15,69 +15,29 @@
  */
 package io.gravitee.am.identityprovider.oauth2.authentication;
 
-import com.nimbusds.jwt.proc.JWTProcessor;
-import io.gravitee.am.common.exception.authentication.BadCredentialsException;
-import io.gravitee.am.common.oauth2.Parameters;
-import io.gravitee.am.common.oauth2.TokenTypeHint;
-import io.gravitee.am.common.oidc.AuthenticationFlow;
-import io.gravitee.am.common.oidc.CustomClaims;
-import io.gravitee.am.common.oidc.ResponseType;
-import io.gravitee.am.common.oidc.StandardClaims;
-import io.gravitee.am.common.utils.SecureRandomString;
-import io.gravitee.am.common.web.UriBuilder;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.DefaultUser;
-import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.identityprovider.api.common.Request;
-import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectAuthenticationProvider;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.IdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.IdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.oidc.OpenIDConnectIdentityProviderConfiguration;
+import io.gravitee.am.identityprovider.common.oauth2.authentication.AbstractOpenIDConnectAuthenticationProvider;
 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.identityprovider.oauth2.authentication.spring.OAuth2GenericAuthenticationProviderConfiguration;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.hmac.MACJWKSourceResolver;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.remote.RemoteJWKSourceResolver;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.rsa.RSAJWKSourceResolver;
-import io.gravitee.am.identityprovider.oauth2.jwt.processor.AbstractKeyProcessor;
-import io.gravitee.am.identityprovider.oauth2.jwt.processor.HMACKeyProcessor;
-import io.gravitee.am.identityprovider.oauth2.jwt.processor.JWKSKeyProcessor;
-import io.gravitee.am.identityprovider.oauth2.jwt.processor.RSAKeyProcessor;
-import io.gravitee.am.identityprovider.oauth2.resolver.KeyResolver;
-import io.gravitee.am.identityprovider.oauth2.utils.URLEncodedUtils;
-import io.gravitee.am.model.http.BasicNameValuePair;
-import io.gravitee.am.model.http.NameValuePair;
-import io.gravitee.common.http.HttpHeaders;
-import io.gravitee.common.http.HttpMethod;
-import io.gravitee.common.http.MediaType;
-import io.reactivex.Maybe;
-import io.vertx.core.json.JsonObject;
-import io.vertx.reactivex.core.buffer.Buffer;
 import io.vertx.reactivex.ext.web.client.WebClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Import;
 import org.springframework.util.Assert;
 
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static io.gravitee.am.common.oidc.Scope.SCOPE_DELIMITER;
+import java.util.Map;
 
 /**
  * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
  * @author GraviteeSource Team
  */
 @Import(OAuth2GenericAuthenticationProviderConfiguration.class)
-public class OAuth2GenericAuthenticationProvider implements OpenIDConnectAuthenticationProvider, InitializingBean {
+public class OAuth2GenericAuthenticationProvider extends AbstractOpenIDConnectAuthenticationProvider<OAuth2GenericIdentityProviderConfiguration> {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(OAuth2GenericAuthenticationProvider.class);
-    private static final String HASH_VALUE_PARAMETER = "urlHash";
-    private static final String ACCESS_TOKEN_PARAMETER = "access_token";
-    private static final String ID_TOKEN_PARAMETER = "id_token";
     private static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
     private static final String TOKEN_ENDPOINT = "token_endpoint";
     private static final String USERINFO_ENDPOINT = "userinfo_endpoint";
@@ -87,174 +47,32 @@ public class OAuth2GenericAuthenticationProvider implements OpenIDConnectAuthent
     private WebClient client;
 
     @Autowired
-    private OAuth2GenericIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Autowired
-    private OAuth2GenericIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Autowired
     private OAuth2GenericIdentityProviderConfiguration configuration;
 
-    private JWTProcessor jwtProcessor;
-
     @Override
-    public Request signInUrl(String redirectUri) {
-        try {
-            UriBuilder builder = UriBuilder.fromHttpUrl(configuration.getUserAuthorizationUri());
-            builder.addParameter(Parameters.CLIENT_ID, configuration.getClientId());
-            builder.addParameter(Parameters.RESPONSE_TYPE, configuration.getResponseType());
-            // append scopes
-            if (configuration.getScopes() != null && !configuration.getScopes().isEmpty()) {
-                builder.addParameter(Parameters.SCOPE, String.join(SCOPE_DELIMITER, configuration.getScopes()));
-            }
-            // nonce parameter is required for implicit/hybrid flow
-            if (!io.gravitee.am.common.oauth2.ResponseType.CODE.equals(configuration.getResponseType())) {
-                builder.addParameter(io.gravitee.am.common.oidc.Parameters.NONCE, SecureRandomString.generate());
-            }
-            // append redirect_uri
-            builder.addParameter(Parameters.REDIRECT_URI, configuration.isEncodeRedirectUri() ? UriBuilder.encodeURIComponent(redirectUri) : redirectUri);
-
-            Request request = new Request();
-            request.setMethod(HttpMethod.GET);
-            request.setUri(builder.buildString());
-            return request;
-        } catch (Exception e) {
-            LOGGER.error("An error occurs while building OpenID Connect Sign In URL", e);
-            return null;
-        }
+    public OpenIDConnectIdentityProviderConfiguration getConfiguration() {
+        return this.configuration;
     }
 
     @Override
-    public AuthenticationFlow authenticationFlow() {
-        return io.gravitee.am.common.oauth2.ResponseType.CODE.equals(configuration.getResponseType()) ? AuthenticationFlow.AUTHORIZATION_CODE_FLOW : AuthenticationFlow.IMPLICIT_FLOW;
+    protected IdentityProviderMapper getIdentityProviderMapper() {
+        return this.mapper;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(Authentication authentication) {
-        return authenticate(authentication)
-                .flatMap(token -> profile(token, authentication));
+    protected IdentityProviderRoleMapper getIdentityProviderRoleMapper() {
+        return this.roleMapper;
     }
 
     @Override
-    public Maybe<User> loadUserByUsername(String username) {
-        return Maybe.empty();
-    }
-
-    private Maybe<Token> authenticate(Authentication authentication) {
-        // implicit flow, retrieve the hashValue of the URL (#access_token=....&token_type=...)
-        if (AuthenticationFlow.IMPLICIT_FLOW.equals(authenticationFlow())){
-            final String hashValue = authentication.getContext().request().parameters().getFirst(HASH_VALUE_PARAMETER);
-            Map<String, String> hashValues = getParams(hashValue.substring(1));
-
-            // implicit flow was used with response_type=id_token token, access token is already fetched, continue
-            if (ResponseType.ID_TOKEN_TOKEN.equals(configuration.getResponseType())) {
-                String accessToken = hashValues.get(ACCESS_TOKEN_PARAMETER);
-                // put the id_token in context for later use
-                authentication.getContext().set(ID_TOKEN_PARAMETER, hashValues.get(ID_TOKEN_PARAMETER));
-                return Maybe.just(new Token(accessToken, TokenTypeHint.ACCESS_TOKEN));
-            }
-
-            // implicit flow was used with response_type=id_token, id token is already fetched, continue
-            if (ResponseType.ID_TOKEN.equals(configuration.getResponseType())) {
-                String idToken = hashValues.get(ID_TOKEN_PARAMETER);
-                return Maybe.just(new Token(idToken, TokenTypeHint.ID_TOKEN));
-            }
-        }
-
-        // authorization code flow, exchange code for an access token
-        // prepare body request parameters
-        final String authorizationCode = authentication.getContext().request().parameters().getFirst(configuration.getCodeParameter());
-        if (authorizationCode == null || authorizationCode.isEmpty()) {
-            LOGGER.debug("Authorization code is missing, skip authentication");
-            return Maybe.error(new BadCredentialsException("Missing authorization code"));
-        }
-        List<NameValuePair> urlParameters = new ArrayList<>();
-        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_ID, configuration.getClientId()));
-        urlParameters.add(new BasicNameValuePair(Parameters.CLIENT_SECRET, configuration.getClientSecret()));
-        urlParameters.add(new BasicNameValuePair(Parameters.REDIRECT_URI, (String) authentication.getContext().get(Parameters.REDIRECT_URI)));
-        urlParameters.add(new BasicNameValuePair(Parameters.CODE, authorizationCode));
-        urlParameters.add(new BasicNameValuePair(Parameters.GRANT_TYPE, "authorization_code"));
-        String bodyRequest = URLEncodedUtils.format(urlParameters);
-
-        return client.postAbs(configuration.getAccessTokenUri())
-                .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(bodyRequest.length()))
-                .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED)
-                .rxSendBuffer(Buffer.buffer(bodyRequest))
-                .toMaybe()
-                .map(httpResponse -> {
-                    if (httpResponse.statusCode() != 200) {
-                        throw new BadCredentialsException(httpResponse.statusMessage());
-                    }
-
-                    JsonObject response = httpResponse.bodyAsJsonObject();
-                    String accessToken = response.getString(ACCESS_TOKEN_PARAMETER);
-                    if (configuration.isUseIdTokenForUserInfo()) {
-                        // put the id_token in context for later use
-                        String idToken = response.getString(ID_TOKEN_PARAMETER);
-                        authentication.getContext().set(ID_TOKEN_PARAMETER, idToken);
-                    }
-                    return new Token(accessToken, TokenTypeHint.ACCESS_TOKEN);
-                });
-
-    }
-
-    private Maybe<User> profile(Token token, Authentication authentication) {
-        // we only have the id_token, try to decode it and create the end-user
-        if (TokenTypeHint.ID_TOKEN.equals(token.getTypeHint())) {
-            return retrieveUserFromIdToken(token.getValue());
-        }
-
-        // if it's an access token but user ask for id token verification, try to decode it and create the end-user
-        if (TokenTypeHint.ACCESS_TOKEN.equals(token.getTypeHint()) && configuration.isUseIdTokenForUserInfo()) {
-            if (authentication.getContext().get(ID_TOKEN_PARAMETER) != null) {
-                String idToken = (String) authentication.getContext().get(ID_TOKEN_PARAMETER);
-                return retrieveUserFromIdToken(idToken);
-            } else {
-                // no suitable value to retrieve user
-                return Maybe.error(new BadCredentialsException("No suitable value to retrieve user information"));
-            }
-        }
-
-        // retrieve user claims from the UserInfo Endpoint
-        return client.getAbs(configuration.getUserProfileUri())
-                .putHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token.getValue())
-                .rxSend()
-                .toMaybe()
-                .map(httpClientResponse -> {
-                    if (httpClientResponse.statusCode() != 200) {
-                        throw new BadCredentialsException(httpClientResponse.statusMessage());
-                    }
-
-                    return createUser(httpClientResponse.bodyAsJsonObject().getMap());
-                });
-    }
-
-    private Maybe<User> retrieveUserFromIdToken(String idToken) {
-        return Maybe.fromCallable(() -> jwtProcessor.process(idToken, null))
-                .onErrorResumeNext(ex -> {
-                    return Maybe.error(new BadCredentialsException(ex.getMessage()));
-                })
-                .map(jwtClaimsSet -> createUser(jwtClaimsSet.getClaims()));
-    }
-
-    private User createUser(Map<String, Object> attributes) {
-        String username = (String) attributes.getOrDefault(StandardClaims.PREFERRED_USERNAME, attributes.get(StandardClaims.SUB));
-        User user = new DefaultUser(username);
-        ((DefaultUser) user).setId((String) attributes.get(StandardClaims.SUB));
-        // set additional information
-        Map<String, Object> additionalInformation = new HashMap<>();
-        additionalInformation.put(StandardClaims.SUB, attributes.get(StandardClaims.SUB));
-        additionalInformation.put(StandardClaims.PREFERRED_USERNAME, username);
-        // apply user mapping
-        additionalInformation.putAll(applyUserMapping(attributes));
-        // update username if user mapping has been changed
-        if (additionalInformation.containsKey(StandardClaims.PREFERRED_USERNAME)) {
-            ((DefaultUser) user).setUsername((String) additionalInformation.get(StandardClaims.PREFERRED_USERNAME));
-        }
-        ((DefaultUser) user).setAdditionalInformation(additionalInformation);
-        // set user roles
-        ((DefaultUser) user).setRoles(applyRoleMapping(attributes));
-        return user;
+    protected WebClient getClient() {
+        return this.client;
     }
 
     @Override
@@ -313,121 +131,4 @@ public class OAuth2GenericAuthenticationProvider implements OpenIDConnectAuthent
             }
         }
     }
-
-    private void generateJWTProcessor(OAuth2GenericIdentityProviderConfiguration configuration) {
-        if (configuration.isUseIdTokenForUserInfo() || ResponseType.ID_TOKEN.equals(configuration.getResponseType())) {
-            if (configuration.getPublicKeyResolver() == null || configuration.getResolverParameter() == null) {
-                throw new IllegalStateException("An public key resolver must be supply to verify the ID Token");
-            }
-
-            final Signature signature = (configuration.getSignature() == null) ? Signature.RSA_RS256 : configuration.getSignature();
-
-            AbstractKeyProcessor keyProcessor = null;
-            // init JWT key source (Remote URL or from configuration file)
-            if (KeyResolver.JWKS_URL.equals(configuration.getPublicKeyResolver())) {
-                keyProcessor = new JWKSKeyProcessor();
-                keyProcessor.setJwkSourceResolver(new RemoteJWKSourceResolver(configuration.getResolverParameter()));
-            } else {
-                // get the corresponding key processor
-                final String resolverParameter = configuration.getResolverParameter();
-                switch (signature) {
-                    case RSA_RS256:
-                    case RSA_RS384:
-                    case RSA_RS512:
-                        keyProcessor = new RSAKeyProcessor();
-                        keyProcessor.setJwkSourceResolver(new RSAJWKSourceResolver(resolverParameter));
-                        break;
-                    case HMAC_HS256:
-                    case HMAC_HS384:
-                    case HMAC_HS512:
-                        keyProcessor = new HMACKeyProcessor();
-                        keyProcessor.setJwkSourceResolver(new MACJWKSourceResolver(resolverParameter));
-                        break;
-                }
-            }
-
-            Assert.notNull(keyProcessor, "A key processor must be set");
-            jwtProcessor = keyProcessor.create(signature);
-        }
-    }
-
-    private Map<String, String> getParams(String query) {
-        Map<String, String> query_pairs = new LinkedHashMap<>();
-        String[] pairs = query.split("&");
-        for (String pair : pairs) {
-            int idx = pair.indexOf("=");
-            query_pairs.put(pair.substring(0, idx), pair.substring(idx + 1));
-        }
-        return query_pairs;
-    }
-
-    private Map<String, Object> applyUserMapping(Map<String, Object> attributes) {
-        if (!mappingEnabled()) {
-            // set default standard claims
-            return Stream.concat(StandardClaims.claims().stream(), CustomClaims.claims().stream())
-                    .filter(claimName -> attributes.containsKey(claimName))
-                    .collect(Collectors.toMap(claimName -> claimName, claimName -> attributes.get(claimName)));
-        }
-
-        Map<String, Object> claims = new HashMap<>();
-        this.mapper.getMappers().forEach((k, v) -> {
-            if (attributes.containsKey(v)) {
-                claims.put(k, attributes.get(v));
-            }
-        });
-        return claims;
-    }
-
-    private List<String> applyRoleMapping(Map<String, Object> attributes) {
-        if (!roleMappingEnabled()) {
-            return Collections.emptyList();
-        }
-
-        Set<String> roles = new HashSet<>();
-        roleMapper.getRoles().forEach((role, users) -> {
-            Arrays.asList(users).forEach(u -> {
-                // role mapping have the following syntax userAttribute=userValue
-                String[] roleMapping = u.split("=",2);
-                String userAttribute = roleMapping[0];
-                String userValue = roleMapping[1];
-                if (attributes.containsKey(userAttribute)) {
-                    Object attribute = attributes.get(userAttribute);
-                    // attribute is a list
-                    if (attribute instanceof Collection && ((Collection) attribute).contains(userValue)) {
-                        roles.add(role);
-                    } else if (userValue.equals(attributes.get(userAttribute))) {
-                        roles.add(role);
-                    }
-                }
-            });
-        });
-
-        return new ArrayList<>(roles);
-    }
-
-    private boolean mappingEnabled() {
-        return this.mapper != null && this.mapper.getMappers() != null && !this.mapper.getMappers().isEmpty();
-    }
-
-    private boolean roleMappingEnabled() {
-        return this.roleMapper != null && this.roleMapper.getRoles() != null && !this.roleMapper.getRoles().isEmpty();
-    }
-
-    private class Token {
-        private String value;
-        private TokenTypeHint typeHint;
-
-        public Token(String value, TokenTypeHint typeHint) {
-            this.value = value;
-            this.typeHint = typeHint;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        public TokenTypeHint getTypeHint() {
-            return typeHint;
-        }
-    }
 }
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..769c25ffae 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
@@ -15,8 +15,8 @@
  */
 package io.gravitee.am.identityprovider.oauth2.authentication.spring;
 
+import io.gravitee.am.identityprovider.common.oauth2.utils.WebClientBuilder;
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderConfiguration;
-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.Autowired;
@@ -31,8 +31,6 @@ import org.springframework.context.annotation.Configuration;
 @Configuration
 public class OAuth2GenericAuthenticationProviderConfiguration {
 
-    private static final String DEFAULT_USER_AGENT = "Gravitee.io-AM/3";
-
     @Autowired
     private Vertx vertx;
 
@@ -42,12 +40,6 @@ public class OAuth2GenericAuthenticationProviderConfiguration {
     @Bean
     @Qualifier("oauthWebClient")
     public WebClient httpClient() {
-        WebClientOptions httpClientOptions = new WebClientOptions();
-        httpClientOptions
-                .setUserAgent(DEFAULT_USER_AGENT)
-                .setConnectTimeout(configuration.getConnectTimeout())
-                .setMaxPoolSize(configuration.getMaxPoolSize());
-
-        return WebClient.create(vertx, httpClientOptions);
+        return WebClientBuilder.build(vertx, configuration);
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/algo/Signature.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/algo/Signature.java
index b0ebc6a0d2..8ef66ec56d 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/algo/Signature.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/algo/Signature.java
@@ -15,27 +15,27 @@
  */
 package io.gravitee.am.identityprovider.oauth2.jwt.algo;
 
-import com.nimbusds.jose.JWSAlgorithm;
+import io.gravitee.am.common.jwt.SignatureAlgorithm;
 
 /**
  * @author David BRASSELY (david.brassely at graviteesource.com)
  * @author GraviteeSource Team
  */
 public enum Signature {
-    RSA_RS256(JWSAlgorithm.RS256),
-    RSA_RS384(JWSAlgorithm.RS384),
-    RSA_RS512(JWSAlgorithm.RS512),
-    HMAC_HS256(JWSAlgorithm.HS256),
-    HMAC_HS384(JWSAlgorithm.HS384),
-    HMAC_HS512(JWSAlgorithm.HS512);
+    RSA_RS256(SignatureAlgorithm.RS256),
+    RSA_RS384(SignatureAlgorithm.RS384),
+    RSA_RS512(SignatureAlgorithm.RS512),
+    HMAC_HS256(SignatureAlgorithm.HS256),
+    HMAC_HS384(SignatureAlgorithm.HS384),
+    HMAC_HS512(SignatureAlgorithm.HS512);
 
-    private JWSAlgorithm alg;
+    private SignatureAlgorithm alg;
 
-    Signature(JWSAlgorithm alg) {
+    Signature(SignatureAlgorithm alg) {
         this.alg = alg;
     }
 
-    public JWSAlgorithm getAlg() {
+    public SignatureAlgorithm getAlg() {
         return alg;
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/JWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/JWKSourceResolver.java
deleted file mode 100644
index d19d5b7d4c..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/JWKSourceResolver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.jwks;
-
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.SecurityContext;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public interface JWKSourceResolver<C extends SecurityContext> {
-
-    JWKSource<C> resolve();
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java
deleted file mode 100644
index b7661edc27..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/remote/RemoteJWKSourceResolver.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.jwks.remote;
-
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.jwk.source.RemoteJWKSet;
-import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.JWKSourceResolver;
-
-import java.net.URL;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class RemoteJWKSourceResolver<C extends SecurityContext> implements JWKSourceResolver<C> {
-
-    private final String url;
-
-    public RemoteJWKSourceResolver(String url) {
-        this.url = url;
-    }
-
-    @Override
-    public JWKSource<C> resolve() {
-        try {
-            return new RemoteJWKSet(new URL(url));
-        } catch (Exception e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java
deleted file mode 100644
index 2efcb62feb..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/jwks/rsa/RSAJWKSourceResolver.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.jwks.rsa;
-
-import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.JWKSet;
-import com.nimbusds.jose.jwk.KeyUse;
-import com.nimbusds.jose.jwk.RSAKey;
-import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.SecurityContext;
-import com.nimbusds.jose.util.Base64;
-import com.nimbusds.jose.util.Base64URL;
-import com.nimbusds.jose.util.X509CertUtils;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.JWKSourceResolver;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Collections;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class RSAJWKSourceResolver<C extends SecurityContext> implements JWKSourceResolver<C> {
-
-    private final JWK jwk;
-
-    public RSAJWKSourceResolver(String publicKey) {
-        X509Certificate cert = X509CertUtils.parse(publicKey);
-        jwk = parse(cert);
-    }
-
-    @Override
-    public JWKSource<C> resolve() {
-        return new ImmutableJWKSet<>(new JWKSet(jwk));
-    }
-
-    private static RSAKey parse(final X509Certificate cert) {
-        if (! (cert.getPublicKey() instanceof RSAPublicKey)) {
-            throw new IllegalStateException("The public key of the X.509 certificate is not RSA");
-        }
-
-        RSAPublicKey publicKey = (RSAPublicKey)cert.getPublicKey();
-
-        try {
-            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
-
-            return new RSAKey.Builder(publicKey)
-                    .keyUse(KeyUse.SIGNATURE)
-                    .x509CertChain(Collections.singletonList(Base64.encode(cert.getEncoded())))
-                    .x509CertSHA256Thumbprint(Base64URL.encode(sha256.digest(cert.getEncoded())))
-                    .build();
-        } catch (NoSuchAlgorithmException e) {
-            throw new IllegalStateException("Couldn't encode x5t parameter: " + e.getMessage(), e);
-        } catch (CertificateEncodingException e) {
-            throw new IllegalStateException("Couldn't encode x5c parameter: " + e.getMessage(), e);
-        }
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/AbstractKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/AbstractKeyProcessor.java
deleted file mode 100644
index efffb06bb6..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/AbstractKeyProcessor.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.processor;
-
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.JWSKeySelector;
-import com.nimbusds.jose.proc.SecurityContext;
-import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
-import com.nimbusds.jwt.proc.DefaultJWTProcessor;
-import com.nimbusds.jwt.proc.JWTProcessor;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-import io.gravitee.am.identityprovider.oauth2.jwt.jwks.JWKSourceResolver;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public abstract class AbstractKeyProcessor<C extends SecurityContext> implements KeyProcessor {
-
-    private JWKSourceResolver<C> jwkSourceResolver;
-
-    @Override
-    public JWTProcessor create(Signature signature) {
-        JWKSource jwkSource = jwkSourceResolver.resolve();
-        ConfigurableJWTProcessor<C> jwtProcessor = new DefaultJWTProcessor<>();
-        jwtProcessor.setJWSKeySelector(jwsKeySelector(jwkSource, signature));
-
-        return jwtProcessor;
-    }
-
-    public void setJwkSourceResolver(JWKSourceResolver<C> jwkSourceResolver) {
-        this.jwkSourceResolver = jwkSourceResolver;
-    }
-
-    abstract JWSKeySelector<C> jwsKeySelector(JWKSource<C> jwkSource, Signature signature);
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/HMACKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/HMACKeyProcessor.java
deleted file mode 100644
index b6e9a48155..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/HMACKeyProcessor.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.processor;
-
-import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jose.JWSHeader;
-import com.nimbusds.jose.jwk.JWKMatcher;
-import com.nimbusds.jose.jwk.KeyType;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.JWSKeySelector;
-import com.nimbusds.jose.proc.JWSVerificationKeySelector;
-import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class HMACKeyProcessor<C extends SecurityContext> extends AbstractKeyProcessor<C> {
-
-    @Override
-    JWSKeySelector<C> jwsKeySelector(JWKSource<C> jwkSource, Signature signature) {
-        return new JWSVerificationKeySelector<C>(signature.getAlg(), jwkSource) {
-            @Override
-            protected JWKMatcher createJWKMatcher(final JWSHeader jwsHeader) {
-
-                if (! getExpectedJWSAlgorithm().equals(jwsHeader.getAlgorithm())) {
-                    // Unexpected JWS alg
-                    return null;
-                } else if (JWSAlgorithm.Family.HMAC_SHA.contains(getExpectedJWSAlgorithm())) {
-                    // HMAC secret matcher
-                    return new JWKMatcher.Builder()
-                            .keyType(KeyType.forAlgorithm(getExpectedJWSAlgorithm()))
-                            .privateOnly(true)
-                            .algorithms(getExpectedJWSAlgorithm(), null)
-                            .build();
-                } else {
-                    return null; // Unsupported algorithm
-                }
-            }
-        };
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/JWKSKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/JWKSKeyProcessor.java
deleted file mode 100644
index fd782e5a55..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/JWKSKeyProcessor.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.processor;
-
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.JWSKeySelector;
-import com.nimbusds.jose.proc.JWSVerificationKeySelector;
-import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class JWKSKeyProcessor<C extends SecurityContext> extends AbstractKeyProcessor<C> {
-
-    @Override
-    JWSKeySelector<C> jwsKeySelector(JWKSource<C> jwkSource, Signature signature) {
-        return new JWSVerificationKeySelector<>(signature.getAlg(), jwkSource);
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/KeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/KeyProcessor.java
deleted file mode 100644
index 0bbb95af76..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/KeyProcessor.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.processor;
-
-import com.nimbusds.jwt.proc.JWTProcessor;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public interface KeyProcessor {
-
-    JWTProcessor create(Signature signature);
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/RSAKeyProcessor.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/RSAKeyProcessor.java
deleted file mode 100644
index 3db63b7961..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/jwt/processor/RSAKeyProcessor.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 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.identityprovider.oauth2.jwt.processor;
-
-import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jose.JWSHeader;
-import com.nimbusds.jose.jwk.JWKMatcher;
-import com.nimbusds.jose.jwk.KeyType;
-import com.nimbusds.jose.jwk.KeyUse;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.JWSKeySelector;
-import com.nimbusds.jose.proc.JWSVerificationKeySelector;
-import com.nimbusds.jose.proc.SecurityContext;
-import io.gravitee.am.identityprovider.oauth2.jwt.algo.Signature;
-
-/**
- * @author David BRASSELY (david.brassely at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class RSAKeyProcessor<C extends SecurityContext> extends AbstractKeyProcessor<C> {
-
-    @Override
-    JWSKeySelector<C> jwsKeySelector(JWKSource<C> jwkSource, Signature signature) {
-        return new JWSVerificationKeySelector<C>(signature.getAlg(), jwkSource) {
-            @Override
-            protected JWKMatcher createJWKMatcher(final JWSHeader jwsHeader) {
-
-                if (! getExpectedJWSAlgorithm().equals(jwsHeader.getAlgorithm())) {
-                    // Unexpected JWS alg
-                    return null;
-                } else if (JWSAlgorithm.Family.RSA.contains(getExpectedJWSAlgorithm()) || JWSAlgorithm.Family.EC.contains(getExpectedJWSAlgorithm())) {
-                    // RSA or EC key matcher
-                    return new JWKMatcher.Builder()
-                            .keyType(KeyType.forAlgorithm(getExpectedJWSAlgorithm()))
-                            .keyUses(KeyUse.SIGNATURE, null)
-                            .algorithms(getExpectedJWSAlgorithm(), null)
-                            .x509CertSHA256Thumbprint(jwsHeader.getX509CertSHA256Thumbprint())
-                            .build();
-                } else {
-                    return null; // Unsupported algorithm
-                }
-            }
-        };
-    }
-}
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/utils/URLEncodedUtils.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/utils/URLEncodedUtils.java
deleted file mode 100644
index 65063667ed..0000000000
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/main/java/io/gravitee/am/identityprovider/oauth2/utils/URLEncodedUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * 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.identityprovider.oauth2.utils;
-
-import io.gravitee.am.model.http.NameValuePair;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
- * @author GraviteeSource Team
- */
-public class URLEncodedUtils {
-
-    public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
-    private static final char QP_SEP_A = '&';
-    private static final String NAME_VALUE_SEPARATOR = "=";
-
-    public static String format(final Iterable<? extends NameValuePair> parameters) {
-        final StringBuilder result = new StringBuilder();
-        for (final NameValuePair parameter : parameters) {
-            final String parameterName = parameter.getName();
-            final String parameterValue = parameter.getValue();
-            if (result.length() > 0) {
-                result.append(QP_SEP_A);
-            }
-            result.append(parameterName);
-            if (parameterValue != null) {
-                result.append(NAME_VALUE_SEPARATOR);
-                result.append(parameterValue);
-            }
-        }
-        return result.toString();
-    }
-
-    public static Map<String, String> format(String query) {
-        Map<String, String> queryPairs = new LinkedHashMap<String, String>();
-        String[] pairs = query.split("&");
-        for (String pair : pairs) {
-            int idx = pair.indexOf("=");
-            queryPairs.put(pair.substring(0, idx), pair.substring(idx + 1));
-        }
-        return queryPairs;
-    }
-
-}
diff --git a/gravitee-am-identityprovider/pom.xml b/gravitee-am-identityprovider/pom.xml
index 60f133e35d..45cd0688f9 100644
--- a/gravitee-am-identityprovider/pom.xml
+++ b/gravitee-am-identityprovider/pom.xml
@@ -34,6 +34,7 @@
 
     <modules>
         <module>gravitee-am-identityprovider-api</module>
+        <module>gravitee-am-identityprovider-common</module>
         <module>gravitee-am-identityprovider-inline</module>
         <module>gravitee-am-identityprovider-ldap</module>
         <module>gravitee-am-identityprovider-github</module>
diff --git a/gravitee-am-plugins-handlers/gravitee-am-plugins-handlers-identityprovider/pom.xml b/gravitee-am-plugins-handlers/gravitee-am-plugins-handlers-identityprovider/pom.xml
index 2da1942575..47020320ae 100644
--- a/gravitee-am-plugins-handlers/gravitee-am-plugins-handlers-identityprovider/pom.xml
+++ b/gravitee-am-plugins-handlers/gravitee-am-plugins-handlers-identityprovider/pom.xml
@@ -42,6 +42,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>io.gravitee.am.identityprovider</groupId>
+            <artifactId>gravitee-am-identityprovider-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>io.gravitee.am.certificate</groupId>
             <artifactId>gravitee-am-certificate-api</artifactId>
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.html b/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.html
index 421e098dce..e6eee85afc 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.html
+++ b/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.html
@@ -31,21 +31,15 @@
         {{displayName(provider)}}
       </mat-card-content>
     </mat-card>
-  </div>
-
-  <div *ngIf="socialProviders">
-    <h4>Social</h4>
-    <div fxLayout="row" fxLayoutWrap fxLayoutGap="10px">
-      <mat-card *ngFor="let provider of socialProviders" fxFlex="30">
-        <mat-card-header fxLayoutAlign="end end" fxLayout="row">
-          <mat-radio-group [(ngModel)]="selectedProviderTypeId" (ngModelChange)="selectProviderType(true)">
-            <mat-radio-button [value]="provider.id"></mat-radio-button>
-          </mat-radio-group>
-        </mat-card-header>
-        <mat-card-content>
-          {{displayName(provider)}}
-        </mat-card-content>
-      </mat-card>
-    </div>
+    <mat-card *ngFor="let provider of socialProviders" fxFlex="30">
+      <mat-card-header fxLayoutAlign="end end" fxLayout="row">
+        <mat-radio-group [(ngModel)]="selectedProviderTypeId" (ngModelChange)="selectProviderType(true)">
+          <mat-radio-button [value]="provider.id"></mat-radio-button>
+        </mat-radio-group>
+      </mat-card-header>
+      <mat-card-content>
+        {{displayName(provider)}}
+      </mat-card-content>
+    </mat-card>
   </div>
 </div>
diff --git a/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.ts b/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.ts
index a27bfce6c1..06480183b8 100644
--- a/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.ts
+++ b/gravitee-am-ui/src/app/domain/settings/providers/creation/steps/step1/step1.component.ts
@@ -30,7 +30,8 @@ export class ProviderCreationStep1Component implements OnInit {
     'azure-ad-am-idp': 'Azure AD',
     'github-am-idp': 'GitHub',
     'facebook-am-idp': 'Facebook',
-    'http-am-idp': 'HTTP'
+    'http-am-idp': 'HTTP',
+    'saml2-generic-am-idp': 'SAML 2.0'
   };
   @Input() provider;
   providers: any[];


Test patch:
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/test/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProviderTest.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/test/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProviderTest.java
index d6412f1478..3668898018 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/test/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProviderTest.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-azure-ad/src/test/java/io/gravitee/am/identityprovider/azure/authentication/AzureADAuthenticationProviderTest.java
@@ -17,15 +17,11 @@ package io.gravitee.am.identityprovider.azure.authentication;
 
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
 import io.gravitee.am.common.jwt.SignatureAlgorithm;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.AuthenticationContext;
-import io.gravitee.am.identityprovider.api.User;
+import io.gravitee.am.identityprovider.api.*;
 import io.gravitee.am.identityprovider.api.common.Request;
 import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderMapper;
-import io.gravitee.am.identityprovider.azure.AzureADIdentityProviderRoleMapper;
-import io.gravitee.am.identityprovider.azure.jwt.jwks.hmac.MACJWKSourceResolver;
-import io.gravitee.am.identityprovider.azure.jwt.processor.HMACKeyProcessor;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.jwks.hmac.MACJWKSourceResolver;
+import io.gravitee.am.identityprovider.common.oauth2.jwt.processor.HMACKeyProcessor;
 import io.gravitee.common.http.HttpMethod;
 import io.gravitee.common.http.HttpStatusCode;
 import io.gravitee.common.util.LinkedMultiValueMap;
@@ -47,7 +43,6 @@ import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
 
@@ -71,14 +66,14 @@ public class AzureADAuthenticationProviderTest {
     @Mock
     protected HttpResponse httpResponse;
 
-    @Mock
-    private AzureADIdentityProviderConfiguration configuration;
+    @Spy
+    private AzureADIdentityProviderConfiguration configuration = new AzureADIdentityProviderConfiguration();
 
     @Mock
-    private AzureADIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Mock
-    private AzureADIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @InjectMocks
     private AzureADAuthenticationProvider provider;
@@ -112,47 +107,50 @@ public class AzureADAuthenticationProviderTest {
             event.next();
         });
 
-        HMACKeyProcessor keyProcessor = new HMACKeyProcessor();
-        keyProcessor.setJwkSourceResolver(new MACJWKSourceResolver(secretKey));
-        provider.setJwtProcessor(keyProcessor.create(SignatureAlgorithm.HS256));
+        when(configuration.getClientSecret()).thenReturn("a_secret");
     }
 
     @Test
-    public void shouldGenerateSignInUrl() {
+    public void shouldGenerateSignInUrl() throws Exception {
+        forceProviderInfoForTest();
+
         // openid scope will be added by default
         when(configuration.getClientId()).thenReturn("testClientId");
         when(configuration.getTenantId()).thenReturn(TEST_TENANT_ID);
-        when(configuration.getScopes()).thenReturn(new HashSet<>());
+       // when(configuration.getScopes()).thenReturn(new HashSet<>());
 
         Request request = provider.signInUrl("https://gravitee.io");
 
         Assert.assertNotNull(request);
         assertEquals(HttpMethod.GET, request.getMethod());
-        assertEquals("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/authorize?client_id=testClientId&response_type=code&scope=openid&redirect_uri=https://gravitee.io", request.getUri());
+        assertEquals("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/authorize?client_id=testClientId&response_type=code&scope=openid profile email&redirect_uri=https://gravitee.io", request.getUri());
         assertNull(request.getHeaders());
     }
 
     @Test
-    public void shouldGenerateSignInUrl_withScope() {
+    public void shouldGenerateSignInUrl_withScope() throws Exception {
 
         when(configuration.getClientId()).thenReturn("testClientId");
         when(configuration.getTenantId()).thenReturn(TEST_TENANT_ID);
         LinkedHashSet<String> scopes = new LinkedHashSet<>(); // LinkedHashSet to preserve order of scopes into the URI
-        scopes.add("profile");
-        scopes.add("email");
+        scopes.add("other_scope");
+        scopes.add("other_scope2");
         // openid scope will be added by default
         when(configuration.getScopes()).thenReturn(scopes);
 
+        forceProviderInfoForTest();
+
         Request request = provider.signInUrl("https://gravitee.io");
 
         Assert.assertNotNull(request);
         assertEquals(HttpMethod.GET, request.getMethod());
-        assertEquals("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/authorize?client_id=testClientId&response_type=code&scope=profile email openid&redirect_uri=https://gravitee.io", request.getUri());
+        assertEquals("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/authorize?client_id=testClientId&response_type=code&scope=other_scope other_scope2 openid profile email&redirect_uri=https://gravitee.io", request.getUri());
         assertNull(request.getHeaders());
     }
 
     @Test
-    public void shouldAuthenticate() {
+    public void shouldAuthenticate() throws Exception {
+        forceProviderInfoForTest();
 
         Authentication authentication = mock(Authentication.class);
         AuthenticationContext authenticationContext = mock(AuthenticationContext.class);
@@ -170,6 +168,8 @@ public class AzureADAuthenticationProviderTest {
         when(configuration.getTenantId()).thenReturn(TEST_TENANT_ID);
 
         when(authentication.getContext().get("redirect_uri")).thenReturn("https://gravitee.io");
+        when(authenticationContext.get("id_token")).thenReturn(jwt);
+
         when(httpResponse.statusCode())
                 .thenReturn(HttpStatusCode.OK_200);
         //:"eyJ0eXAiOiJKV1QiLCJub25jZSI6IkVKRG5GakxSd3FvYVBMdmJwV2FqWkdWNGZSTkh6eXpoMkU4YzdueHJLWVEiLCJhbGciOiJSUzI1NiIsIng1dCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9iNzM4OTY2NS01OGRmLTRmNGMtYTNhMy1lZDVhZGYwYWFmZDgvIiwiaWF0IjoxNTk0OTEyMjUzLCJuYmYiOjE1OTQ5MTIyNTMsImV4cCI6MTU5NDkxNjE1MywiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IkFTUUEyLzhRQUFBQTMxUE85ODNVUkpZVTQwS0ZWWjJYVWhSa0RWdkdUTGZLcTczV21oeVpZdlE9IiwiYW1yIjpbInB3ZCJdLCJhcHBfZGlzcGxheW5hbWUiOiJUZXN0LUVMRS1JRFAtQVpVUkUiLCJhcHBpZCI6ImU0YmQwNmJkLTBhOGItNGFlOS05ZjYzLWNmNWZiODgwMDdhMSIsImFwcGlkYWNyIjoiMSIsImZhbWlseV9uYW1lIjoiTGVsZXUiLCJnaXZlbl9uYW1lIjoiRXJpYyIsImlwYWRkciI6IjgyLjIzOC4yNTUuMTQzIiwibmFtZSI6IkVyaWMgTGVsZXUiLCJvaWQiOiI0YWUwMjM1My0yOWJlLTQyZDMtODQ5OS1kOGMzNTUyYTVjOTIiLCJwbGF0ZiI6IjE0IiwicHVpZCI6IjEwMDMyMDAwQ0Y0MDg0MDUiLCJzY3AiOiJEaXJlY3RvcnkuUmVhZC5BbGwgZW1haWwgb3BlbmlkIHByb2ZpbGUgVXNlci5SZWFkIiwic3ViIjoidmNzdmFfb1Q5M1Z0TnloZngtQXFtT1RwUG9EdzZ0ZFFkVzV1M1N5N1EtZyIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJFVSIsInRpZCI6ImI3Mzg5NjY1LTU4ZGYtNGY0Yy1hM2EzLWVkNWFkZjBhYWZkOCIsInVuaXF1ZV9uYW1lIjoiZXJpYy5sZWxldUBncmF2aXRlZXNvdXJjZS5jb20iLCJ1cG4iOiJlcmljLmxlbGV1QGdyYXZpdGVlc291cmNlLmNvbSIsInV0aSI6Inl5bUdwZF9ZU0VHMklOTEVBaGZzQVEiLCJ2ZXIiOiIxLjAiLCJ4bXNfc3QiOnsic3ViIjoiQmowM3BnWUx1V3MxOXVXVUJpOXhxU21IN1JvS1A3bWpRUURtZHVWUEdoWSJ9LCJ4bXNfdGNkdCI6MTU0MDU1Njc0NH0.BsDjR_rYQAc0NjdgQLtCNc3cAsflhlOZnNdrnbEeIBrDO-VWsasrcIYACzyES611NQmPG3NTj1LR1bs5OYe8IYpgoRoVdLLirE788lpLMGudlMVi7CNuUntPZn6ca5iqlRs2PSpxrdp56BpdQcnYvTru3KEC-IKN5BLgykwo_pmMxSnsgQRyQL_38Z20ClA3IZwLW-TFQ93hLSCZxcZmpZIKhTKsseDobuif2Eq2U-uEPqYINbF38QUcW6QsCDzs3PUN6aeWV-Gr6KhxLjghTKi30EOmsY7QGU-342QFu-iq45_WC3_zU-sceFGT0ZL-97jpoXaqERWIbJVRbTeuXQ","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh1Tjk1SXZQZmVocTM0R3pCRFoxR1hHaXJuTSJ9.eyJhdWQiOiJlNGJkMDZiZC0wYThiLTRhZTktOWY2My1jZjVmYjg4MDA3YTEiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjczODk2NjUtNThkZi00ZjRjLWEzYTMtZWQ1YWRmMGFhZmQ4L3YyLjAiLCJpYXQiOjE1OTQ5MTIyNTMsIm5iZiI6MTU5NDkxMjI1MywiZXhwIjoxNTk0OTE2MTUzLCJmYW1pbHlfbmFtZSI6IkxlbGV1IiwiZ2l2ZW5fbmFtZSI6IkVyaWMiLCJuYW1lIjoiRXJpYyBMZWxldSIsIm9pZCI6IjRhZTAyMzUzLTI5YmUtNDJkMy04NDk5LWQ4YzM1NTJhNWM5MiIsInByZWZlcnJlZF91c2VybmFtZSI6ImVyaWMubGVsZXVAZ3Jhdml0ZWVzb3VyY2UuY29tIiwic3ViIjoiQmowM3BnWUx1V3MxOXVXVUJpOXhxU21IN1JvS1A3bWpRUURtZHVWUEdoWSIsInRpZCI6ImI3Mzg5NjY1LTU4ZGYtNGY0Yy1hM2EzLWVkNWFkZjBhYWZkOCIsInV0aSI6Inl5bUdwZF9ZU0VHMklOTEVBaGZzQVEiLCJ2ZXIiOiIyLjAifQ.c3u2k8L9fE4eQ8uM5UON1BRkqsTCEIF8jULtNC4mjN7eqncOKxtodFMsPZQeHm8P5aBYWL_LxHJfndQt9XOIReQmD57KHpdzJP4ZKacdDml5uJh30_sOx09Vm5uaEc7zos52Y8xfpyRXWLIDo-oQBwBEVnQQhFN6zgI1uvMP6Gl3y0hu-iGEwZYFkRShoaljA-k18mHbLBmllyxHvf4K2oNvftK0z9sWAwpw4beHcM2k9v2fBrQv9Ygwga1w0PsrD9U62cQnQ43f9EOIugChRRdReyTI2zoJvOeyKjo9nvxDbsz47WgpzTR7YzLhE931lF2aj6gih_4IRqBBsukpjQ"}
@@ -194,10 +194,12 @@ public class AzureADAuthenticationProviderTest {
             return true;
         });
 
+        verify(authenticationContext, times(1)).set("id_token", jwt);
         verify(client, times(1)).postAbs("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/token");
     }
     @Test
-    public void shouldAuthenticate_RoleMapping() {
+    public void shouldAuthenticate_RoleMapping() throws Exception {
+        forceProviderInfoForTest();
         Map<String, String[]> roles = new HashMap<>();
         roles.put("admin", new String[] { "preferred_username=john.doe@graviteesource.com"});
         when(roleMapper.getRoles()).thenReturn(roles);
@@ -205,6 +207,7 @@ public class AzureADAuthenticationProviderTest {
         Authentication authentication = mock(Authentication.class);
         AuthenticationContext authenticationContext = mock(AuthenticationContext.class);
         when(authentication.getContext()).thenReturn(authenticationContext);
+        when(authenticationContext.get("id_token")).thenReturn(jwt);
 
         io.gravitee.gateway.api.Request request = mock(io.gravitee.gateway.api.Request.class);
         when(authenticationContext.request()).thenReturn(request);
@@ -241,17 +244,20 @@ public class AzureADAuthenticationProviderTest {
             assertTrue(user.getRoles().contains("admin"));
             return true;
         });
-
+        verify(authenticationContext, times(1)).set("id_token", jwt);
         verify(client, times(1)).postAbs("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/token");
     }
 
     @Test
-    public void shouldAuthenticate_invalidJwt() {
+    public void shouldAuthenticate_invalidJwt() throws Exception {
+        forceProviderInfoForTest();
+
         final String badJwt = "eyJraWQiOiJkZWZhdWx0LWdyYXZpdGVlLUFNLWtleSIsImFsZyI6IkhTMjU2In0.eyJzdWIiOiJzdWJqb2huZG9lIiwiYXVkIjoiYXVkc3Viam9obmRvZSIsImF1dGhfdGltZSI6MTU5NDkxMjU1MywiaXNzIjoiaHR0cDovL2dyYXZpdGVlLmlvL2RvbWFpbi10ZXN0L29pZGMiLCJuYW1lIjoiSm9obiBEb2UiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqb2huLmRvZUBncmF2aXRlZXNvdXJjZS5jb20iLCJleHAiOjE1OTQ5MjY5ODEsImdpdmVuX25hbWUiOiJKb2huIiwiaWF0IjoxNTk0OTEyNTgxLCJmYW1pbHlfbmFtZSI6IkRvZSJ9.Kgr8PkN9GRtfeASpBF1uvUlK14SEQRIk-XtvwloGzdo";
 
         Authentication authentication = mock(Authentication.class);
         AuthenticationContext authenticationContext = mock(AuthenticationContext.class);
         when(authentication.getContext()).thenReturn(authenticationContext);
+        when(authenticationContext.get("id_token")).thenReturn(badJwt);
 
         io.gravitee.gateway.api.Request request = mock(io.gravitee.gateway.api.Request.class);
         when(authenticationContext.request()).thenReturn(request);
@@ -280,6 +286,16 @@ public class AzureADAuthenticationProviderTest {
         obs.awaitTerminalEvent();
         obs.assertError(BadCredentialsException.class);
 
+        verify(authenticationContext, times(1)).set("id_token", badJwt);
         verify(client, times(1)).postAbs("https://login.microsoftonline.com/"+ TEST_TENANT_ID +"/oauth2/v2.0/token");
     }
+
+    // this method is call inside each test method to avoid override of init value by mock/spy
+    private void forceProviderInfoForTest() throws Exception {
+        provider.afterPropertiesSet();
+        // override the KeyProcessor for test purpose
+        HMACKeyProcessor keyProcessor = new HMACKeyProcessor<>();
+        keyProcessor.setJwkSourceResolver(new MACJWKSourceResolver(secretKey));
+        provider.setJwtProcessor(keyProcessor.create(SignatureAlgorithm.HS256));
+    }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/test/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProviderTest.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/test/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProviderTest.java
index e5009446da..a2255fc98a 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/test/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProviderTest.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-facebook/src/test/java/io/gravitee/am/identityprovider/facebook/authentication/FacebookAuthenticationProviderTest.java
@@ -17,13 +17,9 @@ package io.gravitee.am.identityprovider.facebook.authentication;
 
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
 import io.gravitee.am.common.oidc.StandardClaims;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.AuthenticationContext;
-import io.gravitee.am.identityprovider.api.User;
+import io.gravitee.am.identityprovider.api.*;
 import io.gravitee.am.identityprovider.api.common.Request;
 import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderMapper;
-import io.gravitee.am.identityprovider.facebook.FacebookIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.facebook.model.FacebookUser;
 import io.gravitee.common.http.HttpMethod;
 import io.gravitee.common.http.HttpStatusCode;
@@ -70,10 +66,10 @@ public class FacebookAuthenticationProviderTest {
     private FacebookIdentityProviderConfiguration configuration;
 
     @Mock
-    private FacebookIdentityProviderMapper mapper;
+    private DefaultIdentityProviderMapper mapper;
 
     @Mock
-    private FacebookIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @InjectMocks
     private FacebookAuthenticationProvider cut;
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTest.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTest.java
index 9f12cf540d..764e9aa951 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTest.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-github/src/test/java/io/gravitee/am/identityprovider/github/authentication/GithubAuthenticationProviderTest.java
@@ -17,13 +17,9 @@ package io.gravitee.am.identityprovider.github.authentication;
 
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.AuthenticationContext;
-import io.gravitee.am.identityprovider.api.AuthenticationProvider;
-import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.identityprovider.github.GithubIdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.*;
+import io.gravitee.am.identityprovider.common.oauth2.utils.URLEncodedUtils;
 import io.gravitee.am.identityprovider.github.authentication.spring.GithubAuthenticationProviderConfiguration;
-import io.gravitee.am.identityprovider.github.utils.URLEncodedUtils;
 import io.gravitee.common.http.HttpHeaders;
 import io.reactivex.observers.TestObserver;
 import org.junit.Rule;
@@ -54,7 +50,7 @@ public class GithubAuthenticationProviderTest {
     private AuthenticationProvider authenticationProvider;
 
     @Autowired
-    private GithubIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Rule
     public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(19998));
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..e542bc9894 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
@@ -16,9 +16,9 @@
 package io.gravitee.am.identityprovider.github.authentication;
 
 import io.gravitee.am.identityprovider.api.AuthenticationProvider;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.github.GithubIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.github.GithubIdentityProviderMapper;
-import io.gravitee.am.identityprovider.github.GithubIdentityProviderRoleMapper;
 import io.vertx.reactivex.core.Vertx;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -61,12 +61,12 @@ public class GithubAuthenticationProviderTestConfiguration {
     }
 
     @Bean
-    public GithubIdentityProviderRoleMapper roleMapper() {
-        return new GithubIdentityProviderRoleMapper();
+    public DefaultIdentityProviderRoleMapper roleMapper() {
+        return new DefaultIdentityProviderRoleMapper();
     }
 
     @Bean
-    public GithubIdentityProviderMapper mapper() {
-        return new GithubIdentityProviderMapper();
+    public DefaultIdentityProviderMapper mapper() {
+        return new DefaultIdentityProviderMapper();
     }
 }
diff --git a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTest.java b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTest.java
index 99231fa14f..d2d8972372 100644
--- a/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTest.java
+++ b/gravitee-am-identityprovider/gravitee-am-identityprovider-oauth2-generic/src/test/java/io/gravitee/am/identityprovider/oauth2/authentication/OAuth2GenericAuthenticationProviderTest.java
@@ -17,13 +17,9 @@ package io.gravitee.am.identityprovider.oauth2.authentication;
 
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import io.gravitee.am.common.exception.authentication.BadCredentialsException;
-import io.gravitee.am.identityprovider.api.Authentication;
-import io.gravitee.am.identityprovider.api.AuthenticationContext;
-import io.gravitee.am.identityprovider.api.AuthenticationProvider;
-import io.gravitee.am.identityprovider.api.User;
-import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderRoleMapper;
+import io.gravitee.am.identityprovider.api.*;
+import io.gravitee.am.identityprovider.common.oauth2.utils.URLEncodedUtils;
 import io.gravitee.am.identityprovider.oauth2.authentication.spring.OAuth2GenericAuthenticationProviderConfiguration;
-import io.gravitee.am.identityprovider.oauth2.utils.URLEncodedUtils;
 import io.gravitee.common.http.HttpHeaders;
 import io.reactivex.observers.TestObserver;
 import org.junit.Rule;
@@ -54,7 +50,7 @@ public class OAuth2GenericAuthenticationProviderTest {
     private AuthenticationProvider authenticationProvider;
 
     @Autowired
-    private OAuth2GenericIdentityProviderRoleMapper roleMapper;
+    private DefaultIdentityProviderRoleMapper roleMapper;
 
     @Rule
     public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(19999));
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..2dfd722382 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
@@ -16,9 +16,9 @@
 package io.gravitee.am.identityprovider.oauth2.authentication;
 
 import io.gravitee.am.identityprovider.api.AuthenticationProvider;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderMapper;
+import io.gravitee.am.identityprovider.api.DefaultIdentityProviderRoleMapper;
 import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderConfiguration;
-import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderMapper;
-import io.gravitee.am.identityprovider.oauth2.OAuth2GenericIdentityProviderRoleMapper;
 import io.vertx.reactivex.core.Vertx;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -51,13 +51,13 @@ public class OAuth2GenericAuthenticationProviderTestConfiguration {
     }
 
     @Bean
-    public OAuth2GenericIdentityProviderMapper mapper() {
-        return new OAuth2GenericIdentityProviderMapper();
+    public DefaultIdentityProviderMapper mapper() {
+        return new DefaultIdentityProviderMapper();
     }
 
     @Bean
-    public OAuth2GenericIdentityProviderRoleMapper roleMapper() {
-        return new OAuth2GenericIdentityProviderRoleMapper();
+    public DefaultIdentityProviderRoleMapper roleMapper() {
+        return new DefaultIdentityProviderRoleMapper();
     }
 
     @Bean("graviteeProperties")