keycloak-memoizeit

broker mapper mgmt

4/15/2015 12:10:53 PM

Changes

broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java 135(+0 -135)

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 d65081d..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
@@ -20,6 +20,7 @@ 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;
@@ -68,12 +69,12 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> 
     }
 
     @Override
-    public void importNewUser(UserModel user, BrokeredIdentityContext context) {
+    public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
 
     }
 
     @Override
-    public void updateBrokeredUser(UserModel user, BrokeredIdentityContext context) {
+    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 0517b3a..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
@@ -21,6 +21,7 @@ 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;
@@ -28,7 +29,6 @@ import org.keycloak.provider.Provider;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import java.util.Map;
 
 /**
  * @author Pedro Igor
@@ -46,15 +46,9 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
         public Response authenticated(BrokeredIdentityContext context);
     }
 
-    /**
-     *
-     * @param userSession
-     * @param clientSession
-     * @param context
-     */
     void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
-    void importNewUser(UserModel user, BrokeredIdentityContext context);
-    void updateBrokeredUser(UserModel user, 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.
@@ -67,12 +61,6 @@ 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
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 0581eb1..60b1f4a 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
@@ -26,17 +26,13 @@ import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
-import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Flows;
 
@@ -49,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;
 
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 19a0f9d..eb73c0a 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
@@ -19,11 +19,9 @@ package org.keycloak.broker.oidc;
 
 import org.codehaus.jackson.JsonNode;
 import org.jboss.logging.Logger;
-import org.keycloak.RSATokenVerifier;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
@@ -36,6 +34,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.IDToken;
+import org.keycloak.representations.JsonWebToken;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.IdentityBrokerService;
@@ -53,7 +52,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
@@ -178,14 +176,14 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
 
 
 
-        IDToken idToken = validateIdToken(key, encodedIdToken);
+        JsonWebToken idToken = validateIdToken(key, encodedIdToken);
 
         try {
             String id = idToken.getSubject();
             BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
-            String name = idToken.getName();
-            String preferredUsername = idToken.getPreferredUsername();
-            String email = idToken.getEmail();
+            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 (getConfig().getUserInfoUrl() != null && (id == null || name == null || preferredUsername == null || email == null) ) {
                 JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl())
@@ -239,7 +237,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.");
         }
@@ -249,7 +247,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();
@@ -285,16 +283,6 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
     }
 
     @Override
-    public void importNewUser(UserModel user, BrokeredIdentityContext context) {
-
-    }
-
-    @Override
-    public void updateBrokeredUser(UserModel user, BrokeredIdentityContext context) {
-
-    }
-
-    @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
index 32e132d..0dcde56 100755
--- 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,18 +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.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 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}
  * 
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 efee926..b13ff09 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
@@ -4,9 +4,17 @@ import org.jboss.logging.Logger;
 import org.keycloak.ClientConnection;
 import org.keycloak.VerificationException;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 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;
@@ -18,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.resources.flows.Flows;
 import org.keycloak.saml.common.constants.GeneralConstants;
 import org.keycloak.saml.common.constants.JBossSAMLConstants;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
@@ -34,15 +39,9 @@ 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.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
+import org.keycloak.services.resources.flows.Flows;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -63,9 +62,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>
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 aaec932..f88c45d 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
@@ -137,16 +137,6 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
     }
 
     @Override
-    public void importNewUser(UserModel user, BrokeredIdentityContext context) {
-
-    }
-
-    @Override
-    public void updateBrokeredUser(UserModel user, BrokeredIdentityContext context) {
-
-    }
-
-    @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 f830ac1..6987151 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/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index bbb32b4..3f1983c 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.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.services.resources.flows.Flows;
 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 0bdb525..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
@@ -18,6 +18,7 @@ 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;
@@ -142,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 (ProviderConfigProperty 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 4098d33..7550eb8 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -23,10 +23,10 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.ClientConnection;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 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 +34,14 @@ import org.keycloak.events.EventType;
 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;
@@ -71,6 +74,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;
@@ -347,7 +351,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
                 LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdpConfig().getAlias());
             }
         }
-        updatedIdentity.getIdp().updateBrokeredUser(federatedUser, updatedIdentity);
+        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) {
@@ -520,7 +533,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
 
         this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
 
-        updatedIdentity.getIdp().importNewUser(federatedUser, updatedIdentity);
+        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())
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 4fc319a..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
@@ -5,7 +5,6 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
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 978b4fe..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
@@ -5,7 +5,6 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
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 9283ce5..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
@@ -27,7 +27,6 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
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 4ee77e8..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
@@ -28,7 +28,6 @@ import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
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 f8aebe2..b209fce 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
@@ -27,7 +27,6 @@ import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
-import org.keycloak.broker.provider.FederatedIdentity;
 import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
@@ -36,8 +35,6 @@ import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Flows;
@@ -55,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;