keycloak-aplcache

Merge pull request #1146 from patriot1burke/master phased

4/15/2015 12:21:26 PM

Changes

Details

diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
index a0f8309..a231346 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
@@ -18,8 +18,11 @@
 package org.keycloak.broker.provider;
 
 import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 
 import javax.ws.rs.core.Response;
@@ -59,4 +62,19 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> 
     public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
         return null;
     }
+
+    @Override
+    public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
+
+    }
+
+    @Override
+    public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
+
+    }
+
+    @Override
+    public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
+
+    }
 }
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
index ebc2ccd..01d5455 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -18,15 +18,17 @@
 package org.keycloak.broker.provider;
 
 import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.provider.Provider;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.Map;
 
 /**
  * @author Pedro Igor
@@ -38,15 +40,16 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
          * This method should be called by provider after the JAXRS callback endpoint has finished authentication
          * with the remote IDP
          *
-         * @param userNotes notes to add to the UserSessionModel
-         * @param identityProviderConfig provider config
-         * @param federatedIdentity federated identity
-         * @param code relayState or state parameter used to identity the client session
+         * @param context
          * @return
          */
-        public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code);
+        public Response authenticated(BrokeredIdentityContext context);
     }
 
+    void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
+    void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
+    void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
+
     /**
      * JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
      *
@@ -58,17 +61,11 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
      * <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
      * only once during the authentication.</p>
      *
-     * <p>Depending on how the authentication is performed, this method may redirect the user to the identity provider for authentication.
-     * In this case, the response would contain a {@link javax.ws.rs.core.Response} that will be used to redirect the user.</p>
-     *
-     * <p>However, if the authentication flow does not require a redirect to the identity provider (eg.: simple challenge/response mechanism), this method may return a response containing
-     * a {@link FederatedIdentity} representing the identity information for an user. In this case, the authentication flow stops.</p>
-     *
      * @param request The initial authentication request. Contains all the contextual information in order to build an authentication request to the
  *                    identity provider.
      * @return
      */
-    Response handleRequest(AuthenticationRequest request);
+    Response performLogin(AuthenticationRequest request);
 
     /**
      * <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
new file mode 100755
index 0000000..990c6de
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
@@ -0,0 +1,24 @@
+package org.keycloak.broker.provider;
+
+import org.keycloak.models.IdentityProviderMapperModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.ConfiguredProvider;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface IdentityProviderMapper extends Provider, ProviderFactory<IdentityProviderMapper>,ConfiguredProvider {
+    String[] getCompatibleProviders();
+    String getDisplayCategory();
+    String getDisplayType();
+
+    void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
+    void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
+
+
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java
new file mode 100755
index 0000000..e660700
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.broker.provider;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class IdentityProviderMapperSpi implements Spi {
+
+    @Override
+    public String getName() {
+        return "identity-provider-mapper";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return IdentityProviderMapper.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return IdentityProviderMapper.class;
+    }
+
+}
diff --git a/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index d4ef41b..fa44621 100755
--- a/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.broker.provider.IdentityProviderSpi
\ No newline at end of file
+org.keycloak.broker.provider.IdentityProviderSpi
+org.keycloak.broker.provider.IdentityProviderMapperSpi
\ No newline at end of file
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index 0a525a4..95e1f6a 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -25,7 +25,7 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
@@ -33,8 +33,8 @@ import org.keycloak.events.EventType;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.services.messages.Messages;
 import org.keycloak.services.ErrorPage;
+import org.keycloak.services.messages.Messages;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.QueryParam;
@@ -45,8 +45,6 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -87,7 +85,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
     }
 
     @Override
-    public Response handleRequest(AuthenticationRequest request) {
+    public Response performLogin(AuthenticationRequest request) {
         try {
             URI authorizationUrl = createAuthorizationUrl(request).build();
 
@@ -136,7 +134,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
         return null;
     }
 
-    protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
+    protected BrokeredIdentityContext getFederatedIdentity(String response) {
         String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
 
         if (accessToken == null) {
@@ -147,7 +145,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
     }
 
 
-    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+    protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
         return null;
     }
 
@@ -225,14 +223,17 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
                 if (authorizationCode != null) {
                     String response = generateTokenRequest(authorizationCode).asString();
 
-                    HashMap<String, String> userNotes = new HashMap<String, String>();
-                    FederatedIdentity federatedIdentity = getFederatedIdentity(userNotes, response);
+                    BrokeredIdentityContext federatedIdentity = getFederatedIdentity(response);
 
                     if (getConfig().isStoreToken()) {
                         federatedIdentity.setToken(response);
                     }
 
-                    return callback.authenticated(userNotes, getConfig(), federatedIdentity, state);
+                    federatedIdentity.setCode(state);
+                    federatedIdentity.setIdpConfig(getConfig());
+                    federatedIdentity.setIdp(AbstractOAuth2IdentityProvider.this);
+
+                    return callback.authenticated(federatedIdentity);
                 }
             } catch (Exception e) {
                 logger.error("Failed to make identity provider oauth callback", e);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java
index ff89a89..14626c5 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProvider.java
@@ -95,4 +95,6 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
 
 
     }
+
+
 }
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
index 6a47135..b59a427 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/KeycloakOIDCIdentityProviderFactory.java
@@ -19,10 +19,7 @@ package org.keycloak.broker.oidc;
 
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
 import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
-import org.keycloak.util.JsonSerialization;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.Map;
 
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 675361a..2d0c16b 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -21,22 +21,24 @@ import org.codehaus.jackson.JsonNode;
 import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AuthenticationRequest;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
+import org.keycloak.representations.JsonWebToken;
+import org.keycloak.services.ErrorPage;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.ErrorPage;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.PemUtils;
 
@@ -49,7 +51,6 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.security.PublicKey;
-import java.util.Map;
 
 /**
  * @author Pedro Igor
@@ -60,6 +61,9 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
     public static final String OAUTH2_PARAMETER_PROMPT = "prompt";
     public static final String SCOPE_OPENID = "openid";
     public static final String FEDERATED_ID_TOKEN = "FEDERATED_ID_TOKEN";
+    public static final String USER_INFO = "UserInfo";
+    public static final String FEDERATED_ACCESS_TOKEN_RESPONSE = "FEDERATED_ACCESS_TOKEN_RESPONSE";
+    public static final String VALIDATED_ID_TOKEN = "VALIDATED_ID_TOKEN";
 
     public OIDCIdentityProvider(OIDCIdentityProviderConfig config) {
         super(config);
@@ -157,7 +161,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
     }
 
     @Override
-    protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
+    protected BrokeredIdentityContext getFederatedIdentity(String response) {
         AccessTokenResponse tokenResponse = null;
         try {
             tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
@@ -169,21 +173,18 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
 
         String encodedIdToken = tokenResponse.getIdToken();
 
-        notes.put(FEDERATED_ACCESS_TOKEN, accessToken);
-        notes.put(FEDERATED_ID_TOKEN, encodedIdToken);
-        notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken());
-        notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn()));
 
 
-        IDToken idToken = validateIdToken(key, encodedIdToken);
+        JsonWebToken idToken = validateIdToken(key, encodedIdToken);
 
         try {
             String id = idToken.getSubject();
-            String name = idToken.getName();
-            String preferredUsername = idToken.getPreferredUsername();
-            String email = idToken.getEmail();
+            BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
+            String name = (String)idToken.getOtherClaims().get(IDToken.NAME);
+            String preferredUsername = (String)idToken.getOtherClaims().get(IDToken.PREFERRED_USERNAME);
+            String email = (String)idToken.getOtherClaims().get(IDToken.EMAIL);
 
-            if (id == null || name == null || preferredUsername == null || email == null && getConfig().getUserInfoUrl() != null) {
+            if (getConfig().getUserInfoUrl() != null && (id == null || name == null || preferredUsername == null || email == null) ) {
                 JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl())
                         .header("Authorization", "Bearer " + accessToken)
                         .asJson();
@@ -192,9 +193,10 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
                 name = getJsonProperty(userInfo, "name");
                 preferredUsername = getJsonProperty(userInfo, "preferred_username");
                 email = getJsonProperty(userInfo, "email");
+                identity.getContextData().put(USER_INFO, userInfo);
             }
-
-            FederatedIdentity identity = new FederatedIdentity(id);
+            identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
+            identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
 
             identity.setId(id);
             identity.setName(name);
@@ -234,7 +236,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
         return accessToken;
     }
 
-   private IDToken validateIdToken(PublicKey key, String encodedToken) {
+   private JsonWebToken validateIdToken(PublicKey key, String encodedToken) {
         if (encodedToken == null) {
             throw new IdentityBrokerException("No id_token from server.");
         }
@@ -244,7 +246,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
             if (!verify(jws, key)) {
                 throw new IdentityBrokerException("IDToken signature validation failed");
             }
-            IDToken idToken = jws.readJsonContent(IDToken.class);
+            JsonWebToken idToken = jws.readJsonContent(JsonWebToken.class);
 
             String aud = idToken.getAudience();
             String iss = idToken.getIssuer();
@@ -273,6 +275,13 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
     }
 
     @Override
+    public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
+        AccessTokenResponse tokenResponse = (AccessTokenResponse)context.getContextData().get(FEDERATED_ACCESS_TOKEN_RESPONSE);
+        userSession.setNote(FEDERATED_ACCESS_TOKEN, tokenResponse.getToken());
+        userSession.setNote(FEDERATED_ID_TOKEN, tokenResponse.getIdToken());
+    }
+
+    @Override
     protected String getDefaultScopes() {
         return "openid";
     }
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
index 4610140..7c3335e 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -17,7 +17,6 @@
  */
 package org.keycloak.broker.oidc;
 
-import org.codehaus.jackson.annotate.JsonProperty;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
 import org.keycloak.jose.jwk.JWK;
@@ -27,7 +26,6 @@ import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
 import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
 import org.keycloak.util.JsonSerialization;
-import org.keycloak.util.PemUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/broker/oidc/src/test/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProviderTest.java b/broker/oidc/src/test/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProviderTest.java
old mode 100644
new mode 100755
index 615a537..0dcde56
--- a/broker/oidc/src/test/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProviderTest.java
+++ b/broker/oidc/src/test/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProviderTest.java
@@ -5,17 +5,17 @@
  */
 package org.keycloak.broker.oidc;
 
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
 import org.codehaus.jackson.JsonNode;
 import org.junit.Assert;
 import org.junit.Test;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.models.IdentityProviderModel;
 
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Unit test for {@link AbstractOAuth2IdentityProvider}
  * 
@@ -58,45 +58,41 @@ public class AbstractOAuth2IdentityProviderTest {
 	@Test(expected = IdentityBrokerException.class)
 	public void getFederatedIdentity_responseUrlLine_tokenNotFound() {
 		TestProvider tested = getTested();
-		Map<String, String> notes = new HashMap<>();
-		tested.getFederatedIdentity(notes, "cosi=sss");
+		tested.getFederatedIdentity("cosi=sss");
 	}
 
 	@Test(expected = IdentityBrokerException.class)
 	public void getFederatedIdentity_responseJSON_tokenNotFound() {
 		TestProvider tested = getTested();
 		Map<String, String> notes = new HashMap<>();
-		tested.getFederatedIdentity(notes, "{\"cosi\":\"sss\"}");
+		tested.getFederatedIdentity("{\"cosi\":\"sss\"}");
 	}
 
 	@Test(expected = IdentityBrokerException.class)
 	public void getFederatedIdentity_responseJSON_invalidFormat() {
 		TestProvider tested = getTested();
 		Map<String, String> notes = new HashMap<>();
-		tested.getFederatedIdentity(notes, "{\"cosi\":\"sss\"");
+		tested.getFederatedIdentity("{\"cosi\":\"sss\"");
 	}
 
 	@Test(expected = IdentityBrokerException.class)
 	public void getFederatedIdentity_responseJSON_emptyTokenField() {
 		TestProvider tested = getTested();
-		Map<String, String> notes = new HashMap<>();
-		tested.getFederatedIdentity(notes, "{\""
+		tested.getFederatedIdentity("{\""
 				+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"\"}");
 	}
 
 	@Test(expected = IdentityBrokerException.class)
 	public void getFederatedIdentity_responseJSON_nullTokenField() {
 		TestProvider tested = getTested();
-		Map<String, String> notes = new HashMap<>();
-		tested.getFederatedIdentity(notes, "{\""
+		tested.getFederatedIdentity("{\""
 				+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : null}");
 	}
 
 	@Test
 	public void getFederatedIdentity_responseJSON() {
 		TestProvider tested = getTested();
-		Map<String, String> notes = new HashMap<>();
-		FederatedIdentity fi = tested.getFederatedIdentity(notes, "{\""
+		BrokeredIdentityContext fi = tested.getFederatedIdentity("{\""
 				+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"458rt\"}");
 		Assert.assertNotNull(fi);
 		Assert.assertEquals("458rt", fi.getId());
@@ -105,8 +101,7 @@ public class AbstractOAuth2IdentityProviderTest {
 	@Test
 	public void getFederatedIdentity_responseUrlLine() {
 		TestProvider tested = getTested();
-		Map<String, String> notes = new HashMap<>();
-		FederatedIdentity fi = tested.getFederatedIdentity(notes, "cosi=sss&"
+        BrokeredIdentityContext fi = tested.getFederatedIdentity("cosi=sss&"
 				+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "=458rtf&kdesi=ss}");
 		Assert.assertNotNull(fi);
 		Assert.assertEquals("458rtf", fi.getId());
@@ -129,8 +124,8 @@ public class AbstractOAuth2IdentityProviderTest {
 			return "default";
 		}
 
-		protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
-			return new FederatedIdentity(accessToken);
+		protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
+			return new BrokeredIdentityContext(accessToken);
 		};
 
 	};
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index c40c0bb..e2a1902 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -3,9 +3,18 @@ package org.keycloak.broker.saml;
 import org.jboss.logging.Logger;
 import org.keycloak.ClientConnection;
 import org.keycloak.VerificationException;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
+import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.assertion.SubjectType;
+import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
+import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
+import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
@@ -17,9 +26,6 @@ import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
 import org.keycloak.protocol.saml.SAMLRequestParser;
 import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.protocol.saml.SamlProtocolUtils;
-import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.messages.Messages;
-import org.keycloak.services.ErrorPage;
 import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
@@ -33,18 +39,15 @@ import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
 import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
 import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
 import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
-import org.keycloak.dom.saml.v2.assertion.AssertionType;
-import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
-import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
-import org.keycloak.dom.saml.v2.assertion.NameIDType;
-import org.keycloak.dom.saml.v2.assertion.SubjectType;
-import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
-import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
-import org.keycloak.dom.saml.v2.protocol.ResponseType;
-import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.keycloak.services.ErrorPage;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
@@ -62,9 +65,7 @@ import java.io.IOException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.cert.X509Certificate;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -75,10 +76,14 @@ public class SAMLEndpoint {
     public static final String SAML_FEDERATED_SESSION_INDEX = "SAML_FEDERATED_SESSION_INDEX";
     public static final String SAML_FEDERATED_SUBJECT = "SAML_FEDERATED_SUBJECT";
     public static final String SAML_FEDERATED_SUBJECT_NAMEFORMAT = "SAML_FEDERATED_SUBJECT_NAMEFORMAT";
+    public static final String SAML_LOGIN_RESPONSE = "SAML_LOGIN_RESPONSE";
+    public static final String SAML_ASSERTION = "SAML_ASSERTION";
+    public static final String SAML_AUTHN_STATEMENT = "SAML_AUTHN_STATEMENT";
     protected RealmModel realm;
     protected EventBuilder event;
     protected SAMLIdentityProviderConfig config;
     protected IdentityProvider.AuthenticationCallback callback;
+    protected SAMLIdentityProvider provider;
 
     @Context
     private UriInfo uriInfo;
@@ -93,10 +98,11 @@ public class SAMLEndpoint {
     private HttpHeaders headers;
 
 
-    public SAMLEndpoint(RealmModel realm, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
+    public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
         this.realm = realm;
         this.config = config;
         this.callback = callback;
+        this.provider = provider;
     }
 
     @GET
@@ -265,10 +271,11 @@ public class SAMLEndpoint {
                 SubjectType subject = assertion.getSubject();
                 SubjectType.STSubType subType = subject.getSubType();
                 NameIDType subjectNameID = (NameIDType) subType.getBaseID();
-                Map<String, String> notes = new HashMap<>();
-                notes.put(SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
-                if (subjectNameID.getFormat() != null) notes.put(SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
-                FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
+                //Map<String, String> notes = new HashMap<>();
+                BrokeredIdentityContext identity = new BrokeredIdentityContext(subjectNameID.getValue());
+                identity.setCode(relayState);
+                identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType);
+                identity.getContextData().put(SAML_ASSERTION, assertion);
 
                 identity.setUsername(subjectNameID.getValue());
 
@@ -284,23 +291,27 @@ public class SAMLEndpoint {
                 for (Object statement : assertion.getStatements()) {
                     if (statement instanceof AuthnStatementType) {
                         authn = (AuthnStatementType)statement;
+                        identity.getContextData().put(SAML_AUTHN_STATEMENT, authn);
                         break;
                     }
                 }
                 String brokerUserId = config.getAlias() + "." + subjectNameID.getValue();
                 identity.setBrokerUserId(brokerUserId);
+                identity.setIdpConfig(config);
+                identity.setIdp(provider);
                 if (authn != null && authn.getSessionIndex() != null) {
                     identity.setBrokerSessionId(identity.getBrokerUserId() + "." + authn.getSessionIndex());
-                    notes.put(SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
-                }
-                return callback.authenticated(notes, config, identity, relayState);
+                 }
+
+
+                return callback.authenticated(identity);
 
             } catch (Exception e) {
                 throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
             }
+        }
 
 
-        }
 
         private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
             List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index af817c9..98fd6ef 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -19,8 +19,15 @@ package org.keycloak.broker.saml;
 
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
+import org.keycloak.dom.saml.v2.assertion.NameIDType;
+import org.keycloak.dom.saml.v2.assertion.SubjectType;
+import org.keycloak.dom.saml.v2.protocol.ResponseType;
 import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
@@ -47,11 +54,11 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
     @Override
     public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
-        return new SAMLEndpoint(realm, getConfig(), callback);
+        return new SAMLEndpoint(realm, this, getConfig(), callback);
     }
 
     @Override
-    public Response handleRequest(AuthenticationRequest request) {
+    public Response performLogin(AuthenticationRequest request) {
         try {
             UriInfo uriInfo = request.getUriInfo();
             RealmModel realm = request.getRealm();
@@ -113,6 +120,22 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
     }
 
     @Override
+    public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
+        ResponseType responseType = (ResponseType)context.getContextData().get(SAMLEndpoint.SAML_LOGIN_RESPONSE);
+        AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
+        SubjectType subject = assertion.getSubject();
+        SubjectType.STSubType subType = subject.getSubType();
+        NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+        userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
+        if (subjectNameID.getFormat() != null) userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
+        AuthnStatementType authn =  (AuthnStatementType)context.getContextData().get(SAMLEndpoint.SAML_AUTHN_STATEMENT);
+        if (authn != null && authn.getSessionIndex() != null) {
+            userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
+
+        }
+    }
+
+    @Override
     public Response retrieveToken(FederatedIdentityModel identity) {
         return Response.ok(identity.getToken()).build();
     }
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
index 0abf926..98ad908 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
@@ -18,17 +18,17 @@
 package org.keycloak.broker.saml;
 
 import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
-import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
-import org.keycloak.saml.common.exceptions.ParsingException;
-import org.keycloak.saml.common.util.DocumentUtil;
-import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
 import org.keycloak.dom.saml.v2.metadata.EndpointType;
 import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
 import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
 import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
 import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
 import org.keycloak.dom.saml.v2.metadata.KeyTypes;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.common.util.DocumentUtil;
+import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
 import org.w3c.dom.Element;
 
 import javax.xml.namespace.QName;
diff --git a/core/src/main/java/org/keycloak/representations/AddressClaimSet.java b/core/src/main/java/org/keycloak/representations/AddressClaimSet.java
index 4b47b8f..3805f6e 100755
--- a/core/src/main/java/org/keycloak/representations/AddressClaimSet.java
+++ b/core/src/main/java/org/keycloak/representations/AddressClaimSet.java
@@ -7,22 +7,29 @@ import org.codehaus.jackson.annotate.JsonProperty;
 * @version $Revision: 1 $
 */
 public class AddressClaimSet {
-    @JsonProperty("formatted")
+    public static final String FORMATTED = "formatted";
+    public static final String STREET_ADDRESS = "street_address";
+    public static final String LOCALITY = "locality";
+    public static final String REGION = "region";
+    public static final String POSTAL_CODE = "postal_code";
+    public static final String COUNTRY = "country";
+
+    @JsonProperty(FORMATTED)
     protected String formattedAddress;
 
-    @JsonProperty("street_address")
+    @JsonProperty(STREET_ADDRESS)
     protected String streetAddress;
 
-    @JsonProperty("locality")
+    @JsonProperty(LOCALITY)
     protected String locality;
 
-    @JsonProperty("region")
+    @JsonProperty(REGION)
     protected String region;
 
-    @JsonProperty("postal_code")
+    @JsonProperty(POSTAL_CODE)
     protected String postalCode;
 
-    @JsonProperty("country")
+    @JsonProperty(COUNTRY)
     protected String country;
 
     public String getFormattedAddress() {
diff --git a/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java
new file mode 100755
index 0000000..0a96270
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ConfigPropertyRepresentation.java
@@ -0,0 +1,53 @@
+package org.keycloak.representations.idm;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class ConfigPropertyRepresentation {
+    protected String name;
+    protected String label;
+    protected String helpText;
+    protected String type;
+    protected Object defaultValue;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Object getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public String getHelpText() {
+        return helpText;
+    }
+
+    public void setHelpText(String helpText) {
+        this.helpText = helpText;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperRepresentation.java
new file mode 100755
index 0000000..2d59ab8
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperRepresentation.java
@@ -0,0 +1,57 @@
+package org.keycloak.representations.idm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IdentityProviderMapperRepresentation {
+    protected String id;
+    protected String name;
+    protected String identityProviderAlias;
+    protected String identityProviderMapper;
+    protected Map<String, String> config = new HashMap<String, String>();
+
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getIdentityProviderAlias() {
+        return identityProviderAlias;
+    }
+
+    public void setIdentityProviderAlias(String identityProviderAlias) {
+        this.identityProviderAlias = identityProviderAlias;
+    }
+
+    public String getIdentityProviderMapper() {
+        return identityProviderMapper;
+    }
+
+    public void setIdentityProviderMapper(String identityProviderMapper) {
+        this.identityProviderMapper = identityProviderMapper;
+    }
+
+    public Map<String, String> getConfig() {
+        return config;
+    }
+
+    public void setConfig(Map<String, String> config) {
+        this.config = config;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperTypeRepresentation.java
new file mode 100755
index 0000000..c76f6ee
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderMapperTypeRepresentation.java
@@ -0,0 +1,56 @@
+package org.keycloak.representations.idm;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IdentityProviderMapperTypeRepresentation {
+    protected String id;
+    protected String name;
+    protected String category;
+    protected String helpText;
+
+    protected List<ConfigPropertyRepresentation> properties;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(String category) {
+        this.category = category;
+    }
+
+    public String getHelpText() {
+        return helpText;
+    }
+
+    public void setHelpText(String helpText) {
+        this.helpText = helpText;
+    }
+
+    public List<ConfigPropertyRepresentation> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(List<ConfigPropertyRepresentation> properties) {
+        this.properties = properties;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java
index 779f3be..212fe58 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperTypeRepresentation.java
@@ -12,55 +12,7 @@ public class ProtocolMapperTypeRepresentation {
     protected String category;
     protected String helpText;
 
-    public static class ConfigProperty {
-        protected String name;
-        protected String label;
-        protected String helpText;
-        protected String type;
-        protected Object defaultValue;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getType() {
-            return type;
-        }
-
-        public void setType(String type) {
-            this.type = type;
-        }
-
-        public Object getDefaultValue() {
-            return defaultValue;
-        }
-
-        public void setDefaultValue(Object defaultValue) {
-            this.defaultValue = defaultValue;
-        }
-
-        public String getHelpText() {
-            return helpText;
-        }
-
-        public void setHelpText(String helpText) {
-            this.helpText = helpText;
-        }
-    }
-
-    protected List<ConfigProperty> properties;
+    protected List<ConfigPropertyRepresentation> properties;
 
     public String getId() {
         return id;
@@ -94,11 +46,11 @@ public class ProtocolMapperTypeRepresentation {
         this.helpText = helpText;
     }
 
-    public List<ConfigProperty> getProperties() {
+    public List<ConfigPropertyRepresentation> getProperties() {
         return properties;
     }
 
-    public void setProperties(List<ConfigProperty> properties) {
+    public void setProperties(List<ConfigPropertyRepresentation> properties) {
         this.properties = properties;
     }
 }
diff --git a/core/src/main/java/org/keycloak/representations/IDToken.java b/core/src/main/java/org/keycloak/representations/IDToken.java
index 263ccb1..78fa5ce 100755
--- a/core/src/main/java/org/keycloak/representations/IDToken.java
+++ b/core/src/main/java/org/keycloak/representations/IDToken.java
@@ -13,76 +13,96 @@ import java.util.Map;
  * @version $Revision: 1 $
  */
 public class IDToken extends JsonWebToken {
+    public static final String NONCE = "nonce";
+    public static final String SESSION_STATE = "session_state";
+    public static final String NAME = "name";
+    public static final String GIVEN_NAME = "given_name";
+    public static final String FAMILY_NAME = "family_name";
+    public static final String MIDDLE_NAME = "middle_name";
+    public static final String NICKNAME = "nickname";
+    public static final String PREFERRED_USERNAME = "preferred_username";
+    public static final String PROFILE = "profile";
+    public static final String PICTURE = "picture";
+    public static final String WEBSITE = "website";
+    public static final String EMAIL = "email";
+    public static final String EMAIL_VERIFIED = "email_verified";
+    public static final String GENDER = "gender";
+    public static final String BIRTHDATE = "birthdate";
+    public static final String ZONEINFO = "zoneinfo";
+    public static final String LOCALE = "locale";
+    public static final String PHONE_NUMBER = "phone_number";
+    public static final String PHONE_NUMBER_VERIFIED = "phone_number_verified";
+    public static final String ADDRESS = "address";
+    public static final String UPDATED_AT = "updated_at";
+    public static final String CLAIMS_LOCALES = "claims_locales";
     // NOTE!!!  WE used to use @JsonUnwrapped on a UserClaimSet object.  This screws up otherClaims and the won't work
     // anymore.  So don't have any @JsonUnwrapped!
-    @JsonProperty("nonce")
+    @JsonProperty(NONCE)
     protected String nonce;
 
-    @JsonProperty("session_state")
+    @JsonProperty(SESSION_STATE)
     protected String sessionState;
 
-    @JsonProperty("name")
+    @JsonProperty(NAME)
     protected String name;
 
-    @JsonProperty("given_name")
+    @JsonProperty(GIVEN_NAME)
     protected String givenName;
 
-    @JsonProperty("family_name")
+    @JsonProperty(FAMILY_NAME)
     protected String familyName;
 
-    @JsonProperty("middle_name")
+    @JsonProperty(MIDDLE_NAME)
     protected String middleName;
 
-    @JsonProperty("nickname")
+    @JsonProperty(NICKNAME)
     protected String nickName;
 
-    @JsonProperty("preferred_username")
+    @JsonProperty(PREFERRED_USERNAME)
     protected String preferredUsername;
 
-    @JsonProperty("profile")
+    @JsonProperty(PROFILE)
     protected String profile;
 
-    @JsonProperty("picture")
+    @JsonProperty(PICTURE)
     protected String picture;
 
-    @JsonProperty("website")
+    @JsonProperty(WEBSITE)
     protected String website;
 
-    @JsonProperty("email")
+    @JsonProperty(EMAIL)
     protected String email;
 
-    @JsonProperty("email_verified")
+    @JsonProperty(EMAIL_VERIFIED)
     protected Boolean emailVerified;
 
-    @JsonProperty("gender")
+    @JsonProperty(GENDER)
     protected String gender;
 
-    @JsonProperty("birthdate")
+    @JsonProperty(BIRTHDATE)
     protected String birthdate;
 
-    @JsonProperty("zoneinfo")
+    @JsonProperty(ZONEINFO)
     protected String zoneinfo;
 
-    @JsonProperty("locale")
+    @JsonProperty(LOCALE)
     protected String locale;
 
-    @JsonProperty("phone_number")
+    @JsonProperty(PHONE_NUMBER)
     protected String phoneNumber;
 
-    @JsonProperty("phone_number_verified")
+    @JsonProperty(PHONE_NUMBER_VERIFIED)
     protected Boolean phoneNumberVerified;
 
-    @JsonProperty("address")
+    @JsonProperty(ADDRESS)
     protected AddressClaimSet address;
 
-    @JsonProperty("updated_at")
+    @JsonProperty(UPDATED_AT)
     protected Long updatedAt;
 
-    @JsonProperty("claims_locales")
+    @JsonProperty(CLAIMS_LOCALES)
     protected String claimsLocales;
 
-    protected Map<String, Object> otherClaims = new HashMap<String, Object>();
-
     public String getNonce() {
         return nonce;
     }
@@ -259,18 +279,4 @@ public class IDToken extends JsonWebToken {
         this.claimsLocales = claimsLocales;
     }
 
-    /**
-     * This is a map of any other claims and data that might be in the IDToken.  Could be custom claims set up by the auth server
-     *
-     * @return
-     */
-    @JsonAnyGetter
-    public Map<String, Object> getOtherClaims() {
-        return otherClaims;
-    }
-
-    @JsonAnySetter
-    public void setOtherClaims(String name, Object value) {
-        otherClaims.put(name, value);
-    }
 }
diff --git a/core/src/main/java/org/keycloak/representations/JsonWebToken.java b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
index d0a4e9d..8db6ef3 100755
--- a/core/src/main/java/org/keycloak/representations/JsonWebToken.java
+++ b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
@@ -1,10 +1,14 @@
 package org.keycloak.representations;
 
+import org.codehaus.jackson.annotate.JsonAnyGetter;
+import org.codehaus.jackson.annotate.JsonAnySetter;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.keycloak.util.Time;
 
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -29,6 +33,7 @@ public class JsonWebToken implements Serializable {
     protected String type;
     @JsonProperty("azp")
     public String issuedFor;
+    protected Map<String, Object> otherClaims = new HashMap<String, Object>();
 
     public String getId() {
         return id;
@@ -153,4 +158,19 @@ public class JsonWebToken implements Serializable {
         this.issuedFor = issuedFor;
         return this;
     }
+
+    /**
+     * This is a map of any other claims and data that might be in the IDToken.  Could be custom claims set up by the auth server
+     *
+     * @return
+     */
+    @JsonAnyGetter
+    public Map<String, Object> getOtherClaims() {
+        return otherClaims;
+    }
+
+    @JsonAnySetter
+    public void setOtherClaims(String name, Object value) {
+        otherClaims.put(name, value);
+    }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index dc1124a..a2bb17f 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -4,6 +4,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientIdentityProviderMappingModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
@@ -18,6 +19,7 @@ import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentat
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
@@ -327,4 +329,16 @@ public class ModelToRepresentation {
         return rep;
     }
 
+    public static IdentityProviderMapperRepresentation toRepresentation(IdentityProviderMapperModel model) {
+        IdentityProviderMapperRepresentation rep = new IdentityProviderMapperRepresentation();
+        rep.setId(model.getId());
+        rep.setIdentityProviderMapper(model.getIdentityProviderMapper());
+        rep.setIdentityProviderAlias(model.getIdentityProviderAlias());
+        Map<String, String> config = new HashMap<String, String>();
+        config.putAll(model.getConfig());
+        rep.setConfig(config);
+        rep.setName(model.getName());
+        return rep;
+    }
+
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 0671641..00e86f0 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -9,6 +9,7 @@ import org.keycloak.models.ClaimMask;
 import org.keycloak.models.ClientIdentityProviderMappingModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.PasswordPolicy;
@@ -25,6 +26,7 @@ import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentat
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -870,6 +872,16 @@ public class RepresentationToModel {
         return model;
     }
 
+    public static IdentityProviderMapperModel toModel(IdentityProviderMapperRepresentation rep) {
+        IdentityProviderMapperModel model = new IdentityProviderMapperModel();
+        model.setId(rep.getId());
+        model.setName(rep.getName());
+        model.setIdentityProviderAlias(rep.getIdentityProviderAlias());
+        model.setIdentityProviderMapper(rep.getIdentityProviderMapper());
+        model.setConfig(rep.getConfig());
+        return model;
+    }
+
     private static List<ClientIdentityProviderMappingModel> toModel(List<ClientIdentityProviderMappingRepresentation> repIdentityProviders, RealmModel realm) {
         List<ClientIdentityProviderMappingModel> result = new ArrayList<ClientIdentityProviderMappingModel>();
 
diff --git a/model/api/src/main/java/org/keycloak/provider/ConfiguredProvider.java b/model/api/src/main/java/org/keycloak/provider/ConfiguredProvider.java
new file mode 100755
index 0000000..d37afcd
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/provider/ConfiguredProvider.java
@@ -0,0 +1,15 @@
+package org.keycloak.provider;
+
+import org.keycloak.provider.ProviderConfigProperty;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ConfiguredProvider {
+    String getHelpText();
+
+    List<ProviderConfigProperty> getConfigProperties();
+}
diff --git a/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java b/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
new file mode 100755
index 0000000..e3a217d
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/provider/ProviderConfigProperty.java
@@ -0,0 +1,57 @@
+package org.keycloak.provider;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class ProviderConfigProperty {
+    public static final String BOOLEAN_TYPE="boolean";
+    public static final String STRING_TYPE="String";
+    public static final String LIST_TYPE="List";
+
+    protected String name;
+    protected String label;
+    protected String helpText;
+    protected String type;
+    protected Object defaultValue;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Object getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    public String getHelpText() {
+        return helpText;
+    }
+
+    public void setHelpText(String helpText) {
+        this.helpText = helpText;
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
index 11e27dc..c854619 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
@@ -1,8 +1,8 @@
 package org.keycloak.protocol.saml.mappers;
 
 import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.protocol.ProtocolMapper;
 import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
@@ -48,18 +48,18 @@ public class AttributeStatementHelper {
         return attribute;
     }
 
-    public static void setConfigProperties(List<ProtocolMapper.ConfigProperty> configProperties) {
-        ProtocolMapper.ConfigProperty property = new ProtocolMapper.ConfigProperty();
+    public static void setConfigProperties(List<ProviderConfigProperty> configProperties) {
+        ProviderConfigProperty property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.FRIENDLY_NAME);
         property.setLabel(AttributeStatementHelper.FRIENDLY_NAME_LABEL);
         property.setHelpText(AttributeStatementHelper.FRIENDLY_NAME_HELP_TEXT);
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAME);
         property.setLabel("SAML Attribute Name");
         property.setHelpText("SAML Attribute Name");
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT);
         property.setLabel("SAML Attribute NameFormat");
         property.setHelpText("SAML Attribute NameFormat.  Can be basic, URI reference, or unspecified.");
@@ -67,7 +67,7 @@ public class AttributeStatementHelper {
         types.add(AttributeStatementHelper.BASIC);
         types.add(AttributeStatementHelper.URI_REFERENCE);
         types.add(AttributeStatementHelper.UNSPECIFIED);
-        property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+        property.setType(ProviderConfigProperty.LIST_TYPE);
         property.setDefaultValue(types);
         configProperties.add(property);
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
index 5ec2589..5aa2d0a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
@@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.provider.ProviderConfigProperty;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -18,15 +19,15 @@ import java.util.List;
 public class HardcodedAttributeMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
     public static final String PROVIDER_ID = "saml-hardcode-attribute-mapper";
     public static final String ATTRIBUTE_VALUE = "attribute.value";
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
+        ProviderConfigProperty property;
         AttributeStatementHelper.setConfigProperties(configProperties);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(ATTRIBUTE_VALUE);
         property.setLabel("Attribute value");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         property.setHelpText("Value of the attribute you want to hard code.");
         configProperties.add(property);
 
@@ -34,7 +35,7 @@ public class HardcodedAttributeMapper extends AbstractSAMLProtocolMapper impleme
 
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
     @Override
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java
index 15a6d8b..321727a 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedRole.java
@@ -1,6 +1,7 @@
 package org.keycloak.protocol.saml.mappers;
 
 import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.protocol.saml.SamlProtocol;
 
 import java.util.ArrayList;
@@ -17,21 +18,21 @@ import java.util.Map;
 public class HardcodedRole extends AbstractSAMLProtocolMapper {
     public static final String PROVIDER_ID = "saml-hardcode-role-mapper";
     public static final String ATTRIBUTE_VALUE = "attribute.value";
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName("role");
         property.setLabel("Role");
         property.setHelpText("Role name you want to hardcode.");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
     }
 
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
     @Override
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
index eb0e354..677b2bf 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapper;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.protocol.saml.SamlProtocol;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
 import org.keycloak.dom.saml.v2.assertion.AttributeType;
@@ -25,22 +26,22 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
     public static final String PROVIDER_ID = "saml-role-list-mapper";
     public static final String SINGLE_ROLE_ATTRIBUTE = "single";
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAME);
         property.setLabel("Role attribute name");
         property.setDefaultValue("Role");
         property.setHelpText("Name of the SAML attribute you want to put your roles into.  i.e. 'Role', 'memberOf'.");
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.FRIENDLY_NAME);
         property.setLabel(AttributeStatementHelper.FRIENDLY_NAME_LABEL);
         property.setHelpText(AttributeStatementHelper.FRIENDLY_NAME_HELP_TEXT);
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(AttributeStatementHelper.SAML_ATTRIBUTE_NAMEFORMAT);
         property.setLabel("SAML Attribute NameFormat");
         property.setHelpText("SAML Attribute NameFormat.  Can be basic, URI reference, or unspecified.");
@@ -48,13 +49,13 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
         types.add(AttributeStatementHelper.BASIC);
         types.add(AttributeStatementHelper.URI_REFERENCE);
         types.add(AttributeStatementHelper.UNSPECIFIED);
-        property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+        property.setType(ProviderConfigProperty.LIST_TYPE);
         property.setDefaultValue(types);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(SINGLE_ROLE_ATTRIBUTE);
         property.setLabel("Single Role Attribute");
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText("If true, all roles will be stored under one attribute with multiple attribute values.");
         configProperties.add(property);
@@ -78,7 +79,7 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
     }
 
     @Override
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java
index b9deb24..e47ca3d 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/RoleNameMapper.java
@@ -4,6 +4,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper;
 import org.keycloak.protocol.saml.SamlProtocol;
 
@@ -20,31 +21,31 @@ import java.util.Map;
  */
 public class RoleNameMapper extends AbstractOIDCProtocolMapper implements SAMLRoleNameMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     public static final String ROLE_CONFIG = "role";
     public static String NEW_ROLE_NAME = "new.role.name";
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ROLE_CONFIG);
         property.setLabel("Role");
         property.setHelpText("Role name you want changed.  To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(NEW_ROLE_NAME);
         property.setLabel("New Role Name");
         property.setHelpText("The new role name.");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
     }
 
     public static final String PROVIDER_ID = "saml-role-name-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
index a31b5a4..81d52b2 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.provider.ProviderConfigProperty;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -18,11 +19,11 @@ import java.util.List;
  * @version $Revision: 1 $
  */
 public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
         property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
         property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
@@ -34,7 +35,7 @@ public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper imp
     public static final String PROVIDER_ID = "saml-user-attribute-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
     @Override
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
index c9596d0..3034624 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.provider.ProviderConfigProperty;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -18,11 +19,11 @@ import java.util.List;
  * @version $Revision: 1 $
  */
 public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMapper implements SAMLAttributeStatementMapper {
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
         property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL);
         property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
@@ -34,7 +35,7 @@ public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMa
     public static final String PROVIDER_ID = "saml-user-property-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
     @Override
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
index fa95be1..5ebff0d 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
@@ -6,6 +6,7 @@ import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.AddressClaimSet;
 import org.keycloak.representations.IDToken;
@@ -23,21 +24,21 @@ import java.util.Map;
  */
 public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
         configProperties.add(property);
@@ -75,7 +76,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
     }
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
index 000f520..caeeedf 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -23,21 +24,21 @@ import java.util.Map;
  */
 public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
         configProperties.add(property);
@@ -47,7 +48,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
     public static final String PROVIDER_ID = "oidc-full-name-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
index ecea9e8..f0bebf6 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
@@ -8,6 +8,7 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapper;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -24,25 +25,25 @@ import java.util.Map;
  */
 public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     public static final String CLAIM_VALUE = "claim.value";
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
         property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         property.setHelpText("Claim name you want to hard code into the token.  This can be a fully qualified name like 'address.street'.  In this case, a nested json object will be created.");
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(CLAIM_VALUE);
         property.setLabel("Claim value");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         property.setHelpText("Value of the claim you want to hard code.  'true' and 'false can be used for boolean values.");
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
         property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
         List<String> types = new ArrayList(3);
@@ -50,21 +51,21 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
         types.add("long");
         types.add("int");
         types.add("boolean");
-        property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+        property.setType(ProviderConfigProperty.LIST_TYPE);
         property.setDefaultValue(types);
         property.setHelpText("JSON type that should be used for the value of the claim.  long, int, boolean, and String are valid values.");
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
         property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
         configProperties.add(property);
@@ -74,7 +75,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
     public static final String PROVIDER_ID = "oidc-hardcoded-claim-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
index 4bed115..fd1910f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedRole.java
@@ -6,6 +6,7 @@ import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 
 import java.util.ArrayList;
@@ -21,24 +22,24 @@ import java.util.Map;
  */
 public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     public static final String ROLE_CONFIG = "role";
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ROLE_CONFIG);
         property.setLabel("Role");
-        property.setHelpText("Role you want added to the token.  To specify a client role the syntax is clientId.clientRole, i.e. myapp.myrole");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setHelpText("Role you want added to the token.  To specify an application role the syntax is appname.approle, i.e. myapp.myrole");
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
     }
 
     public static final String PROVIDER_ID = "oidc-hardcoded-role-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index 7635a4b..bae8c08 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -5,6 +5,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.ProtocolMapper;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -101,15 +102,15 @@ public class OIDCAttributeMapperHelper {
         return "true".equals(mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN));
     }
 
-    public static void addAttributeConfig(List<ProtocolMapper.ConfigProperty> configProperties) {
-        ProtocolMapper.ConfigProperty property;
-        property = new ProtocolMapper.ConfigProperty();
+    public static void addAttributeConfig(List<ProviderConfigProperty> configProperties) {
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(TOKEN_CLAIM_NAME);
         property.setLabel(TOKEN_CLAIM_NAME_LABEL);
-        property.setType(ProtocolMapper.ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         property.setHelpText("Name of the claim to insert into the token.  This can be a fully qualified name like 'address.street'.  In this case, a nested json object will be created.");
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(JSON_TYPE);
         property.setLabel(JSON_TYPE);
         List<String> types = new ArrayList(3);
@@ -117,21 +118,21 @@ public class OIDCAttributeMapperHelper {
         types.add("long");
         types.add("int");
         types.add("boolean");
-        property.setType(ProtocolMapper.ConfigProperty.LIST_TYPE);
+        property.setType(ProviderConfigProperty.LIST_TYPE);
         property.setDefaultValue(types);
         property.setHelpText("JSON type that should be used to populate the json claim in the token.  long, int, boolean, and String are valid values.");
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(INCLUDE_IN_ID_TOKEN);
         property.setLabel(INCLUDE_IN_ID_TOKEN_LABEL);
-        property.setType(ProtocolMapper.ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(INCLUDE_IN_ID_TOKEN_HELP_TEXT);
         configProperties.add(property);
-        property = new ProtocolMapper.ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(INCLUDE_IN_ACCESS_TOKEN);
         property.setLabel(INCLUDE_IN_ACCESS_TOKEN_LABEL);
-        property.setType(ProtocolMapper.ConfigProperty.BOOLEAN_TYPE);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
         property.setDefaultValue("true");
         property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
         configProperties.add(property);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
index 14c68d6..35bb712 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -23,31 +24,31 @@ import java.util.Map;
  */
 public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     public static final String ROLE_CONFIG = "role";
     public static String NEW_ROLE_NAME = "new.role.name";
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ROLE_CONFIG);
         property.setLabel("Role");
-        property.setHelpText("Role name you want changed.  To reference an client role the syntax is clientId.clientRole, i.e. myapp.myrole");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setHelpText("Role name you want changed.  To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
-        property = new ConfigProperty();
+        property = new ProviderConfigProperty();
         property.setName(NEW_ROLE_NAME);
         property.setLabel("New Role Name");
         property.setHelpText("The new role name.  The new name format corresponds to where in the access token the role will be mapped to.  So, a new name of 'myapp.newname' will map the role to that position in the access token.  A new name of 'newname' will map the role to the realm roles in the token.");
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
     }
 
     public static final String PROVIDER_ID = "oidc-role-name-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
index b38283c..b854351 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
@@ -7,6 +7,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -23,15 +24,15 @@ import java.util.List;
  */
 public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
 
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
         property.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
         property.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         configProperties.add(property);
         OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
 
@@ -40,7 +41,7 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
     public static final String PROVIDER_ID = "oidc-usermodel-attribute-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
index 15573ac..8b5eb2f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
@@ -6,6 +6,7 @@ import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.representations.IDToken;
 
@@ -21,14 +22,14 @@ import java.util.List;
  * @version $Revision: 1 $
  */
 public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
 
     static {
-        ConfigProperty property;
-        property = new ConfigProperty();
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
         property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
         property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL);
-        property.setType(ConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
         property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
         configProperties.add(property);
         OIDCAttributeMapperHelper.addAttributeConfig(configProperties);
@@ -37,7 +38,7 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
     public static final String PROVIDER_ID = "oidc-usermodel-property-mapper";
 
 
-    public List<ConfigProperty> getConfigProperties() {
+    public List<ProviderConfigProperty> getConfigProperties() {
         return configProperties;
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
index 45b22bf..1539e24 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
@@ -1,134 +1,135 @@
-package org.keycloak.protocol.oidc.mappers;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.UserSessionModel;
-import org.keycloak.protocol.ProtocolMapperUtils;
-import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.representations.AccessToken;
-import org.keycloak.representations.IDToken;
-
-/**
- * Mappings UserSessionModel.note to an ID Token claim.
- *
- * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- */
-public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
-
-    private static final List<ConfigProperty> configProperties = new ArrayList<ConfigProperty>();
-
-    static {
-        ConfigProperty property;
-        property = new ConfigProperty();
-        property.setName(ProtocolMapperUtils.USER_SESSION_NOTE);
-        property.setLabel(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_LABEL);
-        property.setHelpText(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_HELP_TEXT);
-        property.setType(ConfigProperty.STRING_TYPE);
-        configProperties.add(property);
-        property = new ConfigProperty();
-        property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
-        property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
-        property.setType(ConfigProperty.STRING_TYPE);
-        property.setHelpText("Name of the claim to insert into the token.  This can be a fully qualified name like 'address.street'.  In this case, a nested json object will be created.");
-        configProperties.add(property);
-        property = new ConfigProperty();
-        property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
-        property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
-        property.setType(ConfigProperty.STRING_TYPE);
-        property.setDefaultValue(ConfigProperty.STRING_TYPE);
-        property.setHelpText("JSON type that should be used to populate the json claim in the token.  long, int, boolean, and String are valid values.");
-        configProperties.add(property);
-        property = new ConfigProperty();
-        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
-        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
-        property.setDefaultValue("true");
-        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
-        configProperties.add(property);
-        property = new ConfigProperty();
-        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
-        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
-        property.setType(ConfigProperty.BOOLEAN_TYPE);
-        property.setDefaultValue("true");
-        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
-        configProperties.add(property);
-
-    }
-
-    public static final String PROVIDER_ID = "oidc-usersessionmodel-note-mapper";
-
-
-    public List<ConfigProperty> getConfigProperties() {
-        return configProperties;
-    }
-
-    @Override
-    public String getId() {
-        return PROVIDER_ID;
-    }
-
-    @Override
-    public String getDisplayType() {
-        return "User Session Note";
-    }
-
-    @Override
-    public String getDisplayCategory() {
-        return TOKEN_MAPPER_CATEGORY;
-    }
-
-    @Override
-    public String getHelpText() {
-        return "Map a custom user session note to a token claim.";
-    }
-
-    @Override
-    public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
-                                            UserSessionModel userSession, ClientSessionModel clientSession) {
-        if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token;
-
-        setClaim(token, mappingModel, userSession);
-        return token;
-    }
-
-    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
-        String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
-        String noteValue = userSession.getNote(noteName);
-        if (noteValue == null) return;
-        OIDCAttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
-    }
-
-    @Override
-    public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
-        if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token;
-        setClaim(token, mappingModel, userSession);
-        return token;
-    }
-
-    public static ProtocolMapperModel createClaimMapper(String name,
-                                                        String userSessionNote,
-                                                        String tokenClaimName, String jsonType,
-                                                        boolean consentRequired, String consentText,
-                                                        boolean accessToken, boolean idToken) {
-        ProtocolMapperModel mapper = new ProtocolMapperModel();
-        mapper.setName(name);
-        mapper.setProtocolMapper(PROVIDER_ID);
-        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
-        mapper.setConsentRequired(consentRequired);
-        mapper.setConsentText(consentText);
-        Map<String, String> config = new HashMap<String, String>();
-        config.put(ProtocolMapperUtils.USER_SESSION_NOTE, userSessionNote);
-        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
-        config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType);
-        if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
-        if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
-        mapper.setConfig(config);
-        return mapper;
-    }
-}
+package org.keycloak.protocol.oidc.mappers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.IDToken;
+
+/**
+ * Mappings UserSessionModel.note to an ID Token claim.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
+
+    private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+    static {
+        ProviderConfigProperty property;
+        property = new ProviderConfigProperty();
+        property.setName(ProtocolMapperUtils.USER_SESSION_NOTE);
+        property.setLabel(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_LABEL);
+        property.setHelpText(ProtocolMapperUtils.USER_SESSION_MODEL_NOTE_HELP_TEXT);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
+        configProperties.add(property);
+        property = new ProviderConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME);
+        property.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
+        property.setHelpText("Name of the claim to insert into the token.  This can be a fully qualified name like 'address.street'.  In this case, a nested json object will be created.");
+        configProperties.add(property);
+        property = new ProviderConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.JSON_TYPE);
+        property.setLabel(OIDCAttributeMapperHelper.JSON_TYPE);
+        property.setType(ProviderConfigProperty.STRING_TYPE);
+        property.setDefaultValue(ProviderConfigProperty.STRING_TYPE);
+        property.setHelpText("JSON type that should be used to populate the json claim in the token.  long, int, boolean, and String are valid values.");
+        configProperties.add(property);
+        property = new ProviderConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
+        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_LABEL);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
+        property.setDefaultValue("true");
+        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN_HELP_TEXT);
+        configProperties.add(property);
+        property = new ProviderConfigProperty();
+        property.setName(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
+        property.setLabel(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_LABEL);
+        property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
+        property.setDefaultValue("true");
+        property.setHelpText(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT);
+        configProperties.add(property);
+
+    }
+
+    public static final String PROVIDER_ID = "oidc-usersessionmodel-note-mapper";
+
+
+    public List<ProviderConfigProperty> getConfigProperties() {
+        return configProperties;
+    }
+
+    @Override
+    public String getId() {
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public String getDisplayType() {
+        return "User Session Note";
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return TOKEN_MAPPER_CATEGORY;
+    }
+
+    @Override
+    public String getHelpText() {
+        return "Map a custom user session note to a token claim.";
+    }
+
+    @Override
+    public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
+                                            UserSessionModel userSession, ClientSessionModel clientSession) {
+        if (!OIDCAttributeMapperHelper.includeInAccessToken(mappingModel)) return token;
+
+        setClaim(token, mappingModel, userSession);
+        return token;
+    }
+
+    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {
+        String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
+        String noteValue = userSession.getNote(noteName);
+        if (noteValue == null) return;
+        OIDCAttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
+    }
+
+    @Override
+    public IDToken transformIDToken(IDToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession) {
+        if (!OIDCAttributeMapperHelper.includeInIDToken(mappingModel)) return token;
+        setClaim(token, mappingModel, userSession);
+        return token;
+    }
+
+    public static ProtocolMapperModel createClaimMapper(String name,
+                                                        String userSessionNote,
+                                                        String tokenClaimName, String jsonType,
+                                                        boolean consentRequired, String consentText,
+                                                        boolean accessToken, boolean idToken) {
+        ProtocolMapperModel mapper = new ProtocolMapperModel();
+        mapper.setName(name);
+        mapper.setProtocolMapper(PROVIDER_ID);
+        mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+        mapper.setConsentRequired(consentRequired);
+        mapper.setConsentText(consentText);
+        Map<String, String> config = new HashMap<String, String>();
+        config.put(ProtocolMapperUtils.USER_SESSION_NOTE, userSessionNote);
+        config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
+        config.put(OIDCAttributeMapperHelper.JSON_TYPE, jsonType);
+        if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
+        if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+        mapper.setConfig(config);
+        return mapper;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
index d986dec..2016d5d 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapper.java
@@ -1,71 +1,16 @@
 package org.keycloak.protocol;
 
+import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.Provider;
 import org.keycloak.provider.ProviderFactory;
 
-import java.util.List;
-
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
-public interface ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper> {
+public interface ProtocolMapper extends Provider, ProviderFactory<ProtocolMapper>,ConfiguredProvider {
     String getProtocol();
     String getDisplayCategory();
     String getDisplayType();
-    String getHelpText();
-
-    public static class ConfigProperty {
-        public static final String BOOLEAN_TYPE="boolean";
-        public static final String STRING_TYPE="String";
-        public static final String LIST_TYPE="List";
-
-        protected String name;
-        protected String label;
-        protected String helpText;
-        protected String type;
-        protected Object defaultValue;
-
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        public String getLabel() {
-            return label;
-        }
-
-        public void setLabel(String label) {
-            this.label = label;
-        }
-
-        public String getType() {
-            return type;
-        }
-
-        public void setType(String type) {
-            this.type = type;
-        }
-
-        public Object getDefaultValue() {
-            return defaultValue;
-        }
-
-        public void setDefaultValue(Object defaultValue) {
-            this.defaultValue = defaultValue;
-        }
-
-        public String getHelpText() {
-            return helpText;
-        }
-
-        public void setHelpText(String helpText) {
-            this.helpText = helpText;
-        }
-    }
 
-    List<ConfigProperty> getConfigProperties();
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index fc87343..fcc1d63 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -2,28 +2,40 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.broker.provider.IdentityProviderMapper;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientIdentityProviderMappingModel;
 import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
+import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
+import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.services.ErrorResponse;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.social.SocialIdentityProvider;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
@@ -31,6 +43,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -45,6 +58,8 @@ public class IdentityProviderResource {
     private final KeycloakSession session;
     private final IdentityProviderModel identityProviderModel;
 
+    @Context private UriInfo uriInfo;
+
     public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel) {
         this.realm = realm;
         this.session = session;
@@ -56,6 +71,7 @@ public class IdentityProviderResource {
     @NoCache
     @Produces(MediaType.APPLICATION_JSON)
     public IdentityProviderRepresentation getIdentityProvider() {
+        this.auth.requireView();
         IdentityProviderRepresentation rep = ModelToRepresentation.toRepresentation(this.identityProviderModel);
 
         return rep;
@@ -75,6 +91,7 @@ public class IdentityProviderResource {
 
     @PUT
     @Consumes(MediaType.APPLICATION_JSON)
+    @NoCache
     public Response update(IdentityProviderRepresentation providerRep) {
         try {
             this.auth.requireManage();
@@ -163,6 +180,7 @@ public class IdentityProviderResource {
 
     @GET
     @Path("export")
+    @NoCache
     public Response export(@Context UriInfo uriInfo, @QueryParam("format") String format) {
         try {
             this.auth.requireView();
@@ -173,6 +191,97 @@ public class IdentityProviderResource {
         }
     }
 
+    @GET
+    @Path("mapper-types")
+    @NoCache
+    public List<IdentityProviderMapperTypeRepresentation> getMapperTypes() {
+        this.auth.requireView();
+        KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+        List<IdentityProviderMapperTypeRepresentation> types = new LinkedList<>();
+        List<ProviderFactory> factories = sessionFactory.getProviderFactories(IdentityProviderMapper.class);
+        for (ProviderFactory factory : factories) {
+            IdentityProviderMapper mapper = (IdentityProviderMapper)factory;
+            for (String type : mapper.getCompatibleProviders()) {
+                if (type.equals(identityProviderModel.getProviderId())) {
+                    IdentityProviderMapperTypeRepresentation rep = new IdentityProviderMapperTypeRepresentation();
+                    rep.setId(mapper.getId());
+                    rep.setCategory(mapper.getDisplayCategory());
+                    rep.setName(mapper.getDisplayType());
+                    rep.setHelpText(mapper.getHelpText());
+                    List<ProviderConfigProperty> configProperties = mapper.getConfigProperties();
+                    for (ProviderConfigProperty prop : configProperties) {
+                        ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
+                        propRep.setName(prop.getName());
+                        propRep.setLabel(prop.getLabel());
+                        propRep.setType(prop.getType());
+                        propRep.setDefaultValue(prop.getDefaultValue());
+                        propRep.setHelpText(prop.getHelpText());
+                        rep.getProperties().add(propRep);
+                    }
+                    types.add(rep);
+
+                }
+            }
+        }
+        return types;
+    }
+
+    @GET
+    @Path("mappers")
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    public List<IdentityProviderMapperRepresentation> getMappers() {
+        this.auth.requireView();
+        List<IdentityProviderMapperRepresentation> mappers = new LinkedList<>();
+        for (IdentityProviderMapperModel model : realm.getIdentityProviderMappersByAlias(identityProviderModel.getAlias())) {
+            mappers.add(ModelToRepresentation.toRepresentation(model));
+        }
+        return mappers;
+    }
+
+    @POST
+    @Path("mappers")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response addMapper(IdentityProviderMapperRepresentation mapper) {
+        auth.requireManage();
+        IdentityProviderMapperModel model = RepresentationToModel.toModel(mapper);
+        model = realm.addIdentityProviderMapper(model);
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
+
+    }
+
+    @GET
+    @NoCache
+    @Path("mappers/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public IdentityProviderMapperRepresentation getMapperById(@PathParam("id") String id) {
+        auth.requireView();
+        IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        return ModelToRepresentation.toRepresentation(model);
+    }
+
+    @PUT
+    @NoCache
+    @Path("mappers/{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void update(@PathParam("id") String id, IdentityProviderMapperRepresentation rep) {
+        auth.requireManage();
+        IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        model = RepresentationToModel.toModel(rep);
+        realm.updateIdentityProviderMapper(model);
+    }
+
+    @DELETE
+    @NoCache
+    @Path("mappers/{id}")
+    public void delete(@PathParam("id") String id) {
+        auth.requireManage();
+        IdentityProviderMapperModel model = realm.getIdentityProviderMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        realm.removeIdentityProviderMapper(model);
+    }
 
     private void removeClientIdentityProviders(List<ClientModel> clients, IdentityProviderModel identityProvider) {
         for (ClientModel clientModel : clients) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 322f60d..f535b19 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -15,8 +15,10 @@ import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.LoginProtocolFactory;
 import org.keycloak.protocol.ProtocolMapper;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.provider.Spi;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
 import org.keycloak.social.SocialIdentityProvider;
@@ -141,9 +143,10 @@ public class ServerInfoAdminResource {
             rep.setName(mapper.getDisplayType());
             rep.setHelpText(mapper.getHelpText());
             rep.setCategory(mapper.getDisplayCategory());
-            rep.setProperties(new LinkedList<ProtocolMapperTypeRepresentation.ConfigProperty>());
-            for (ProtocolMapper.ConfigProperty prop : mapper.getConfigProperties()) {
-                ProtocolMapperTypeRepresentation.ConfigProperty propRep = new ProtocolMapperTypeRepresentation.ConfigProperty();
+            rep.setProperties(new LinkedList<ConfigPropertyRepresentation>());
+            List<ProviderConfigProperty> configProperties = mapper.getConfigProperties();
+            for (ProviderConfigProperty prop : configProperties) {
+                ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
                 propRep.setName(prop.getName());
                 propRep.setLabel(prop.getLabel());
                 propRep.setType(prop.getType());
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 051aa04..0d4b38b 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -22,10 +22,11 @@ import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.ClientConnection;
 import org.keycloak.broker.provider.AuthenticationRequest;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.broker.provider.IdentityProviderMapper;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
@@ -34,11 +35,14 @@ import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.ProtocolMapper;
 import org.keycloak.protocol.oidc.TokenManager;
 import org.keycloak.provider.ProviderFactory;
 import org.keycloak.services.managers.AppAuthManager;
@@ -72,6 +76,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
 import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
@@ -131,7 +136,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         try {
             ClientSessionCode clientSessionCode = parseClientSessionCode(code);
             IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
-            Response response = identityProvider.handleRequest(createAuthenticationRequest(providerId, clientSessionCode));
+            Response response = identityProvider.performLogin(createAuthenticationRequest(providerId, clientSessionCode));
 
             if (response != null) {
                 this.event.success();
@@ -239,10 +244,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         return getToken(providerId, true);
     }
 
-    public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code) {
+    public Response authenticated(BrokeredIdentityContext context) {
         ClientSessionCode clientCode = null;
+        IdentityProviderModel identityProviderConfig = context.getIdpConfig();
         try {
-            clientCode = parseClientSessionCode(code);
+            clientCode = parseClientSessionCode(context.getCode());
         } catch (Exception e) {
             return redirectToErrorPage(Messages.IDENTITY_PROVIDER_AUTHENTICATION_FAILED, e, identityProviderConfig.getProviderId());
 
@@ -252,32 +258,27 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             if (isDebugEnabled()) {
                 LOGGER.debugf("Token will not be stored for identity provider [%s].", providerId);
             }
-            federatedIdentity.setToken(null);
+            context.setToken(null);
         }
 
-        federatedIdentity.setIdentityProviderId(providerId);
         ClientSessionModel clientSession = clientCode.getClientSession();
-        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, federatedIdentity.getId(),
-                federatedIdentity.getUsername(), federatedIdentity.getToken());
+        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, context.getId(),
+                context.getUsername(), context.getToken());
 
         this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
                 .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
-                .detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentity.getUsername());
+                .detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
 
         UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
 
         // Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
         if (clientSession.getUserSession() != null) {
-            UserSessionModel userSession = clientSession.getUserSession();
-            for (Map.Entry<String, String> entry : userNotes.entrySet()) {
-                userSession.setNote(entry.getKey(), entry.getValue());
-            }
-            return performAccountLinking(clientSession, providerId, federatedIdentityModel, federatedUser);
+            return performAccountLinking(clientSession, context, federatedIdentityModel, federatedUser);
         }
 
         if (federatedUser == null) {
             try {
-                federatedUser = createUser(federatedIdentity);
+                federatedUser = createUser(context);
 
                 if (identityProviderConfig.isUpdateProfileFirstLogin()) {
                     if (isDebugEnabled()) {
@@ -290,18 +291,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             }
         }
 
-        updateFederatedIdentity(federatedIdentity, federatedUser);
+        updateFederatedIdentity(context, federatedUser);
 
         UserSessionModel userSession = this.session.sessions()
-                .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false, federatedIdentity.getBrokerSessionId(), federatedIdentity.getBrokerUserId());
+                .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false, context.getBrokerSessionId(), context.getBrokerUserId());
 
         this.event.user(federatedUser);
         this.event.session(userSession);
 
         TokenManager.attachClientSession(userSession, clientSession);
-        for (Map.Entry<String, String> entry : userNotes.entrySet()) {
-            userSession.setNote(entry.getKey(), entry.getValue());
-        }
+        context.getIdp().attachUserSession(userSession, clientSession, context);
         userSession.setNote(BROKER_PROVIDER_ID, providerId);
 
         if (isDebugEnabled()) {
@@ -312,17 +311,17 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
                 this.uriInfo, event);
     }
 
-    private Response performAccountLinking(ClientSessionModel clientSession, String providerId, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
+    private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
         this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING);
 
         if (federatedUser != null) {
-            return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, providerId);
+            return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias());
         }
 
         UserModel authenticatedUser = clientSession.getUserSession().getUser();
 
         if (isDebugEnabled()) {
-            LOGGER.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, providerId, authenticatedUser);
+            LOGGER.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, context.getIdpConfig().getAlias(), authenticatedUser);
         }
 
         if (!authenticatedUser.isEnabled()) {
@@ -336,14 +335,14 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
 
         this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel);
+        context.getIdp().attachUserSession(clientSession.getUserSession(), clientSession, context);
 
         this.event.success();
-
         return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
     }
 
-    private void updateFederatedIdentity(FederatedIdentity updatedIdentity, UserModel federatedUser) {
-        FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, updatedIdentity.getIdentityProviderId(), this.realmModel);
+    private void updateFederatedIdentity(BrokeredIdentityContext updatedIdentity, UserModel federatedUser) {
+        FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, updatedIdentity.getIdpConfig().getAlias(), this.realmModel);
 
         // Skip DB write if tokens are null or equal
         if (!ObjectUtil.isEqualOrNull(updatedIdentity.getToken(), federatedIdentityModel.getToken())) {
@@ -352,9 +351,19 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
             this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
 
             if (isDebugEnabled()) {
-                LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdentityProviderId());
+                LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdpConfig().getAlias());
             }
         }
+        updatedIdentity.getIdp().updateBrokeredUser(session, realmModel, federatedUser, updatedIdentity);
+        Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(updatedIdentity.getIdpConfig().getAlias());
+        if (mappers != null) {
+            KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+            for (IdentityProviderMapperModel mapper : mappers) {
+                IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
+                target.updateBrokeredUser(session, realmModel, federatedUser, mapper, updatedIdentity);
+            }
+        }
+
     }
 
     private ClientSessionCode parseClientSessionCode(String code) {
@@ -476,8 +485,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         throw new IdentityBrokerException("Configuration for identity provider [" + providerId + "] not found.");
     }
 
-    private UserModel createUser(FederatedIdentity updatedIdentity) {
-        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdentityProviderId(), updatedIdentity.getId(),
+    private UserModel createUser(BrokeredIdentityContext updatedIdentity) {
+        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdpConfig().getAlias(), updatedIdentity.getId(),
                 updatedIdentity.getUsername(), updatedIdentity.getToken());
         // Check if no user already exists with this username or email
         UserModel existingUser = null;
@@ -495,9 +504,9 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         if (this.realmModel.isRegistrationEmailAsUsername() && !Validation.isEmpty(updatedIdentity.getEmail())) {
             username = updatedIdentity.getEmail();
         } else if (username == null) {
-            username = updatedIdentity.getIdentityProviderId() + "." + updatedIdentity.getId();
+            username = updatedIdentity.getIdpConfig().getAlias() + "." + updatedIdentity.getId();
         } else {
-            username = updatedIdentity.getIdentityProviderId() + "." + updatedIdentity.getUsername();
+            username = updatedIdentity.getIdpConfig().getAlias() + "." + updatedIdentity.getUsername();
         }
         if (username != null) {
             username = username.trim();
@@ -521,12 +530,23 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         }
 
         federatedUser.setEnabled(true);
+        federatedUser.setEmail(updatedIdentity.getEmail());
         federatedUser.setFirstName(updatedIdentity.getFirstName());
         federatedUser.setLastName(updatedIdentity.getLastName());
-        federatedUser.setEmail(updatedIdentity.getEmail());
 
         this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
 
+        updatedIdentity.getIdp().importNewUser(session, realmModel, federatedUser, updatedIdentity);
+        Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(updatedIdentity.getIdpConfig().getAlias());
+        if (mappers != null) {
+            KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+            for (IdentityProviderMapperModel mapper : mappers) {
+                IdentityProviderMapper target = (IdentityProviderMapper)sessionFactory.getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
+                target.importNewUser(session, realmModel, federatedUser, mapper, updatedIdentity);
+            }
+        }
+
+
         this.event.clone().user(federatedUser).event(EventType.REGISTER)
                 .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
                 .detail(Details.IDENTITY_PROVIDER_USERNAME, updatedIdentity.getUsername())
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
index de54cc5..cc9364f 100755
--- a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
@@ -4,7 +4,7 @@ import org.codehaus.jackson.JsonNode;
 import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
@@ -25,13 +25,13 @@ public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider imp
         config.setUserInfoUrl(PROFILE_URL);
     }
 
-    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+    protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
         try {
             JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
 
             String id = getJsonProperty(profile, "id");
 
-            FederatedIdentity user = new FederatedIdentity(id);
+            BrokeredIdentityContext user = new BrokeredIdentityContext(id);
 
             String email = getJsonProperty(profile, "email");
 
@@ -59,6 +59,8 @@ public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider imp
             }
 
             user.setName(firstName + lastName);
+            user.setIdpConfig(getConfig());
+            user.setIdp(this);
 
             return user;
         } catch (Exception e) {
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
index d3ff667..ec56d15 100755
--- a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -4,7 +4,7 @@ import org.codehaus.jackson.JsonNode;
 import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
@@ -26,15 +26,17 @@ public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider imple
     }
 
     @Override
-    protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+    protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
         try {
             JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
 
-            FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
+            BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
 
             user.setUsername(getJsonProperty(profile, "login"));
             user.setName(getJsonProperty(profile, "name"));
             user.setEmail(getJsonProperty(profile, "email"));
+            user.setIdpConfig(getConfig());
+            user.setIdp(this);
 
             return user;
         } catch (Exception e) {
diff --git a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
index 298612e..911a55c 100755
--- a/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
+++ b/social/linkedin/src/main/java/org/keycloak/social/linkedin/LinkedInIdentityProvider.java
@@ -26,7 +26,7 @@ import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
@@ -52,18 +52,20 @@ public class LinkedInIdentityProvider extends AbstractOAuth2IdentityProvider imp
 	}
 
 	@Override
-	protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+	protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
 		log.debug("doGetFederatedIdentity()");
 		try {
 			JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
 
-			FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
+            BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "id"));
 
 			user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "publicProfileUrl")));
 			user.setName(getJsonProperty(profile, "formattedName"));
 			user.setEmail(getJsonProperty(profile, "emailAddress"));
+            user.setIdpConfig(getConfig());
+            user.setIdp(this);
 
-			return user;
+            return user;
 		} catch (Exception e) {
 			throw new IdentityBrokerException("Could not obtain user profile from github.", e);
 		}
diff --git a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
index 40fb32f..f834464 100755
--- a/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
+++ b/social/stackoverflow/src/main/java/org/keycloak/social/stackoverflow/StackoverflowIdentityProvider.java
@@ -27,7 +27,7 @@ import org.codehaus.jackson.JsonNode;
 import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.util.SimpleHttp;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
@@ -53,8 +53,10 @@ public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvide
 		config.setUserInfoUrl(PROFILE_URL);
 	}
 
+
+
 	@Override
-	protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
+	protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
 		log.debug("doGetFederatedIdentity()");
 		try {
 
@@ -64,14 +66,17 @@ public class StackoverflowIdentityProvider extends AbstractOAuth2IdentityProvide
 			}
 			JsonNode profile = SimpleHttp.doGet(URL).asJson().get("items").get(0);
 
-			FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "user_id"));
+            BrokeredIdentityContext user = new BrokeredIdentityContext(getJsonProperty(profile, "user_id"));
 
 			user.setUsername(extractUsernameFromProfileURL(getJsonProperty(profile, "link")));
 			user.setName(unescapeHtml3(getJsonProperty(profile, "display_name")));
 			// email is not provided
 			// user.setEmail(getJsonProperty(profile, "email"));
+            user.setIdpConfig(getConfig());
+            user.setIdp(this);
+
 
-			return user;
+            return user;
 		} catch (Exception e) {
 			throw new IdentityBrokerException("Could not obtain user profile from Stackoverflow: " + e.getMessage(), e);
 		}
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
index 9d1d411..bda5d8e 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
@@ -26,7 +26,7 @@ import org.keycloak.ClientConnection;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
-import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
@@ -52,7 +52,6 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
-import java.util.HashMap;
 
 import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
 
@@ -73,7 +72,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
     }
 
     @Override
-    public Response handleRequest(AuthenticationRequest request) {
+    public Response performLogin(AuthenticationRequest request) {
         try {
             Twitter twitter = new TwitterFactory().getInstance();
             twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
@@ -135,7 +134,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
                 AccessToken oAuthAccessToken = twitter.getOAuthAccessToken(requestToken, verifier);
                 twitter4j.User twitterUser = twitter.verifyCredentials();
 
-                FederatedIdentity identity = new FederatedIdentity(Long.toString(twitterUser.getId()));
+                BrokeredIdentityContext identity = new BrokeredIdentityContext(Long.toString(twitterUser.getId()));
 
                 identity.setUsername(twitterUser.getScreenName());
                 identity.setName(twitterUser.getName());
@@ -150,8 +149,10 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
                 tokenBuilder.append("}");
 
                 identity.setToken(tokenBuilder.toString());
+                identity.setCode(state);
+                identity.setIdpConfig(getConfig());
 
-                return callback.authenticated(new HashMap<String, String>(), getConfig(), identity, state);
+                return callback.authenticated(identity);
             } catch (Exception e) {
                 logger.error("Could get user profile from twitter.", e);
             }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java
index f785926..c0e8194 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java
@@ -34,7 +34,7 @@ public class CustomIdentityProvider extends AbstractIdentityProvider<IdentityPro
     }
 
     @Override
-    public Response handleRequest(AuthenticationRequest request) {
+    public Response performLogin(AuthenticationRequest request) {
         return null;
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java
index 4127668..bbead3c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java
@@ -35,7 +35,7 @@ public class CustomSocialProvider extends AbstractIdentityProvider<IdentityProvi
     }
 
     @Override
-    public Response handleRequest(AuthenticationRequest request) {
+    public Response performLogin(AuthenticationRequest request) {
         return null;
     }