keycloak-aplcache

Changes

Details

diff --git a/common/src/main/java/org/keycloak/common/util/ObjectUtil.java b/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
index 1ade852..bec8acf 100644
--- a/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
+++ b/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
@@ -24,4 +24,8 @@ public class ObjectUtil {
 
         return str1.equals(str2);
     }
+
+    public static String capitalize(String str) {
+        return str.substring(0, 1).toUpperCase() + str.substring(1);
+    }
 }
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml b/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
index 549bc84..78d9a4b 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
@@ -818,45 +818,21 @@ $ keytool -import -alias yourdomain -keystore keycloak.jks -file your-certificat
     </section>
 
     <section>
-        <title>Adding Keycloak server in Domain Mode</title>
+        <title>Keycloak server in Domain Mode</title>
         <para>
             In domain mode, you start the server with the "domain" command instead of the "standalone" command.  In this case, the Keycloak subsystem is
             defined in domain/configuration/domain.xml instead of standalone/configuration.standalone.xml.  Inside domain.xml, you will see more than one
-            profile.  A Keycloak subsystem can be defined in zero or more of those profiles.
+            profile.  The Keycloak subsystem is defined for all initial profiles.
         </para>
         <para>
-            To enable Keycloak for a server profile edit domain/configuration/domain.xml. To the <literal>extensions</literal>
-            element add the Keycloak extension:
-<programlisting><![CDATA[
-<extensions>
-    ...
-    <extension module="org.keycloak.keycloak-subsystem"/>
-</extensions>
-]]></programlisting>
-            Then you need to add the server to the required server profiles. By default WildFly starts two servers
-            in the main-server-group which uses the full profile. To add Keycloak for this profile add the Keycloak
-            subsystem to the <literal>profile</literal> element with <literal>name</literal> full:
-<programlisting><![CDATA[
-<profile name="full">
-    ...
-    <subsystem xmlns="urn:jboss:domain:keycloak:1.0">
-        <auth-server name="main-auth-server">
-            <enabled>true</enabled>
-            <web-context>auth</web-context>
-        </auth-server>
-    </subsystem>
-]]></programlisting>
+            THe server is also added to server profiles. By default two servers are started
+            in the main-server-group which uses the full profile.
         </para>
         <para>
-            To configure the server copy <literal>standalone/configuration/keycloak-server.json</literal> to
-            <literal>domain/servers/&lt;SERVER NAME&gt;/configuration</literal>. The configuration should be identical
+            You need to make sure <literal>domain/servers/&lt;SERVER NAME&gt;/configuration</literal> is identical
             for all servers in a group.
         </para>
         <para>
-            Follow the <link linkend='clustering'>Clustering</link> section of the documentation to configure Keycloak
-            for clustering. In domain mode it doesn't make much sense to not configure Keycloak in cluster mode.
-        </para>
-        <para>
             To deploy custom providers and themes you should deploys these as modules and make sure the modules are
             available to all servers in the group. See <link linkend='providers'>Providers</link> and
             <link linkend='themes'>Themes</link> sections for more information on how to do this.
diff --git a/events/api/src/main/java/org/keycloak/events/Errors.java b/events/api/src/main/java/org/keycloak/events/Errors.java
index 34c5979..b0cbc6a 100755
--- a/events/api/src/main/java/org/keycloak/events/Errors.java
+++ b/events/api/src/main/java/org/keycloak/events/Errors.java
@@ -44,8 +44,7 @@ public interface Errors {
 
     String NOT_ALLOWED = "not_allowed";
 
-    String FEDERATED_IDENTITY_EMAIL_EXISTS = "federated_identity_email_exists";
-    String FEDERATED_IDENTITY_USERNAME_EXISTS = "federated_identity_username_exists";
+    String FEDERATED_IDENTITY_EXISTS = "federated_identity_account_exists";
     String SSL_REQUIRED = "ssl_required";
 
     String USER_SESSION_NOT_FOUND = "user_session_not_found";
diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index 5cffe78..b75728b 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -48,6 +48,8 @@ public enum EventType {
     SEND_VERIFY_EMAIL_ERROR(true),
     SEND_RESET_PASSWORD(true),
     SEND_RESET_PASSWORD_ERROR(true),
+    SEND_IDENTITY_PROVIDER_LINK(true),
+    SEND_IDENTITY_PROVIDER_LINK_ERROR(true),
     RESET_PASSWORD(true),
     RESET_PASSWORD_ERROR(true),
 
@@ -66,8 +68,6 @@ public enum EventType {
     IDENTITY_PROVIDER_RESPONSE_ERROR(false),
     IDENTITY_PROVIDER_RETRIEVE_TOKEN(false),
     IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR(false),
-    IDENTITY_PROVIDER_ACCCOUNT_LINKING(false),
-    IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false),
     IMPERSONATE(true),
     CUSTOM_REQUIRED_ACTION(true),
     CUSTOM_REQUIRED_ACTION_ERROR(true),
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
index 02923d9..f77eb38 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
@@ -12,8 +12,8 @@
         <form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
 
                 <div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
-                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
-                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="linkAccount">${msg("confirmLinkIdpContinue", idpAlias)}</button>
+                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="updateProfile" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
+                    <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="linkAccount" value="linkAccount">${msg("confirmLinkIdpContinue", idpAlias)}</button>
                 </div>
 
         </form>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
index ab3e83e..0ba0686 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
@@ -5,10 +5,10 @@
     <#elseif section = "header">
         ${msg("emailLinkIdpTitle", idpAlias)}
     <#elseif section = "form">
-        <p class="instruction">
+        <p id="instruction1" class="instruction">
             ${msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.name)}
         </p>
-        <p class="instruction">
+        <p id="instruction2" class="instruction">
             ${msg("emailLinkIdp2")} <a href="${url.firstBrokerLoginUrl}">${msg("doClickHere")}</a> ${msg("emailLinkIdp3")}
         </p>
     </#if>
diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
index 3a04fbf..25b0166 100755
--- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
+++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
@@ -21,6 +21,7 @@ import javax.mail.internet.MimeMultipart;
 
 import org.jboss.logging.Logger;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.common.util.ObjectUtil;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
 import org.keycloak.email.freemarker.beans.EventBean;
@@ -89,7 +90,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
         attributes.put("link", link);
         attributes.put("linkExpiration", expirationInMinutes);
 
-        String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+        String realmName = ObjectUtil.capitalize(realm.getName());
         attributes.put("realmName", realmName);
 
         send("passwordResetSubject", "password-reset.ftl", attributes);
@@ -102,12 +103,12 @@ public class FreeMarkerEmailProvider implements EmailProvider {
         attributes.put("link", link);
         attributes.put("linkExpiration", expirationInMinutes);
 
-        String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+        String realmName = ObjectUtil.capitalize(realm.getName());
         attributes.put("realmName", realmName);
 
         BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
         String idpAlias = brokerContext.getIdpConfig().getAlias();
-        idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+        idpAlias = ObjectUtil.capitalize(idpAlias);
 
         attributes.put("identityProviderContext", brokerContext);
         attributes.put("identityProviderAlias", idpAlias);
@@ -123,7 +124,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
         attributes.put("link", link);
         attributes.put("linkExpiration", expirationInMinutes);
 
-        String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+        String realmName = ObjectUtil.capitalize(realm.getName());
         attributes.put("realmName", realmName);
 
         send("executeActionsSubject", "executeActions.ftl", attributes);
@@ -137,7 +138,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
         attributes.put("link", link);
         attributes.put("linkExpiration", expirationInMinutes);
 
-        String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+        String realmName = ObjectUtil.capitalize(realm.getName());
         attributes.put("realmName", realmName);
 
         send("emailVerificationSubject", "email-verification.ftl", attributes);
@@ -253,7 +254,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
     private String toCamelCase(EventType event){
         StringBuilder sb = new StringBuilder("event");
         for(String s : event.name().toString().toLowerCase().split("_")){
-            sb.append(s.substring(0,1).toUpperCase()).append(s.substring(1));
+            sb.append(ObjectUtil.capitalize(s));
         }
         return sb.toString();
     }
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index ef8cc74..d1b4df9 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -22,6 +22,7 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
 import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.common.util.ObjectUtil;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
 import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
@@ -288,7 +289,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
             case LOGIN_IDP_LINK_EMAIL:
                 BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
                 String idpAlias = brokerContext.getIdpConfig().getAlias();
-                idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+                idpAlias = ObjectUtil.capitalize(idpAlias);
 
                 attributes.put("brokerContext", brokerContext);
                 attributes.put("idpAlias", idpAlias);
@@ -470,7 +471,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
     public Response createIdpLinkEmailPage() {
         BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
         String idpAlias = brokerContext.getIdpConfig().getAlias();
-        idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+        idpAlias = ObjectUtil.capitalize(idpAlias);;
         setMessage(MessageType.WARNING, Messages.LINK_IDP, idpAlias);
 
         return createResponse(LoginFormsPages.LOGIN_IDP_LINK_EMAIL);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index a874f5e..98e960c 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -25,8 +25,10 @@ public class DefaultAuthenticationFlows {
 
     public static final String CLIENT_AUTHENTICATION_FLOW = "clients";
     public static final String FIRST_BROKER_LOGIN_FLOW = "first broker login";
+    public static final String FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW = "Handle Existing Account";
 
     public static final String IDP_REVIEW_PROFILE_CONFIG_ALIAS = "review profile config";
+    public static final String IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS = "create unique user config";
 
     public static void addFlows(RealmModel realm) {
         if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm);
@@ -347,7 +349,7 @@ public class DefaultAuthenticationFlows {
 
 
         AuthenticatorConfigModel createUserIfUniqueConfig = new AuthenticatorConfigModel();
-        createUserIfUniqueConfig.setAlias("create unique user config");
+        createUserIfUniqueConfig.setAlias(IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
         config = new HashMap<>();
         config.put("require.password.update.after.registration", "false");
         createUserIfUniqueConfig.setConfig(config);
@@ -366,7 +368,7 @@ public class DefaultAuthenticationFlows {
         AuthenticationFlowModel linkExistingAccountFlow = new AuthenticationFlowModel();
         linkExistingAccountFlow.setTopLevel(false);
         linkExistingAccountFlow.setBuiltIn(true);
-        linkExistingAccountFlow.setAlias("Handle Existing Account");
+        linkExistingAccountFlow.setAlias(FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW);
         linkExistingAccountFlow.setDescription("Handle what to do if there is existing account with same email/username like authenticated identity provider");
         linkExistingAccountFlow.setProviderId("basic-flow");
         linkExistingAccountFlow = realm.addAuthenticationFlow(linkExistingAccountFlow);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java
index ffb2300..b4ee957 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java
@@ -10,11 +10,12 @@ import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo;
 import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
 import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
 import org.keycloak.models.AuthenticatorConfigModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.utils.FormMessage;
 import org.keycloak.services.messages.Messages;
 
 /**
@@ -78,6 +79,15 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator
                     .setError(Messages.FEDERATED_IDENTITY_EXISTS, duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue())
                     .createErrorPage();
             context.challenge(challengeResponse);
+
+            if (context.getExecution().isRequired()) {
+                context.getEvent()
+                        .user(duplication.getExistingUserId())
+                        .detail("existing_" + duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue())
+                        .removeDetail(Details.AUTH_METHOD)
+                        .removeDetail(Details.AUTH_TYPE)
+                        .error(Errors.FEDERATED_IDENTITY_EXISTS);
+            }
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
index d6bf10f..ae28d3e 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
@@ -14,6 +14,10 @@ import org.keycloak.authentication.authenticators.broker.util.SerializedBrokered
 import org.keycloak.broker.provider.BrokeredIdentityContext;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.Constants;
@@ -52,6 +56,15 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator 
         String link = UriBuilder.fromUri(context.getActionUrl())
                 .queryParam(Constants.KEY, clientSession.getNote(Constants.VERIFY_EMAIL_KEY))
                 .build().toString();
+
+        EventBuilder event = context.getEvent().clone().event(EventType.SEND_IDENTITY_PROVIDER_LINK)
+                .user(existingUser)
+                .detail(Details.USERNAME, existingUser.getUsername())
+                .detail(Details.EMAIL, existingUser.getEmail())
+                .detail(Details.CODE_ID, clientSession.getId())
+                .removeDetail(Details.AUTH_METHOD)
+                .removeDetail(Details.AUTH_TYPE);
+
         long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction());
         try {
 
@@ -60,15 +73,11 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator 
                     .setUser(existingUser)
                     .setAttribute(EmailProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext)
                     .sendConfirmIdentityBrokerLink(link, expiration);
-//            event.clone().event(EventType.SEND_RESET_PASSWORD)
-//                    .user(user)
-//                    .detail(Details.USERNAME, username)
-//                    .detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, context.getClientSession().getId()).success();
+
+            event.success();
         } catch (EmailException e) {
-//            event.clone().event(EventType.SEND_RESET_PASSWORD)
-//                    .detail(Details.USERNAME, username)
-//                    .user(user)
-//                    .error(Errors.EMAIL_SEND_FAILED);
+            event.error(Errors.EMAIL_SEND_FAILED);
+
             logger.error("Failed to send email to confirm identity broker linking", e);
             Response challenge = context.form()
                     .setError(Messages.EMAIL_SENT_ERROR)
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpUsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpUsernamePasswordForm.java
index b293d1b..5e732d8 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpUsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpUsernamePasswordForm.java
@@ -37,7 +37,7 @@ public class IdpUsernamePasswordForm extends UsernamePasswordForm {
         // Restore formData for the case of error
         setupForm(context, formData, existingUser);
 
-        return validatePassword(context, formData);
+        return validatePassword(context, existingUser, formData);
     }
 
     protected LoginFormsProvider setupForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData, UserModel existingUser) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
index 294d220..0506f21 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java
@@ -71,12 +71,16 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
             context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return true;
         }
+        return false;
+    }
+
+    public boolean enabledUser(AuthenticationFlowContext context, UserModel user) {
         if (!user.isEnabled()) {
             context.getEvent().user(user);
             context.getEvent().error(Errors.USER_DISABLED);
             Response challengeResponse = disabledUser(context);
             context.failureChallenge(AuthenticationFlowError.USER_DISABLED, challengeResponse);
-            return true;
+            return false;
         }
         if (context.getRealm().isBruteForceProtected()) {
             if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
@@ -84,13 +88,13 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
                 context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
                 Response challengeResponse = temporarilyDisabledUser(context);
                 context.failureChallenge(AuthenticationFlowError.USER_TEMPORARILY_DISABLED, challengeResponse);
-                return true;
+                return false;
             }
         }
-        return false;
+        return true;
     }
 
-    public boolean validateUser(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
+    public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
         String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
         if (username == null) {
             context.getEvent().error(Errors.USER_NOT_FOUND);
@@ -117,7 +121,18 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
             return false;
         }
 
-        if (invalidUser(context, user)) return false;
+        if (invalidUser(context, user)){
+            return false;
+        }
+
+        if (!validatePassword(context, user, inputData)){
+            return false;
+        }
+
+        if(!enabledUser(context, user)){
+            return false;
+        }
+
         String rememberMe = inputData.getFirst("rememberMe");
         boolean remember = rememberMe != null && rememberMe.equalsIgnoreCase("on");
         if (remember) {
@@ -130,29 +145,27 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth
         return true;
     }
 
-    public boolean validatePassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
+    public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap<String, String> inputData) {
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
         if (password == null || password.isEmpty()) {
-            if (context.getUser() != null) {
-                context.getEvent().user(context.getUser());
-            }
-            context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
-            Response challengeResponse = invalidCredentials(context);
-            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
-            context.clearUser();
+            invalidPassword(context, user);
             return false;
         }
         credentials.add(UserCredentialModel.password(password));
-        boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+        boolean valid = context.getSession().users().validCredentials(context.getRealm(), user, credentials);
         if (!valid) {
-            context.getEvent().user(context.getUser());
-            context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
-            Response challengeResponse = invalidCredentials(context);
-            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
-            context.clearUser();
+            invalidPassword(context, user);
             return false;
         }
         return true;
     }
+
+    private void invalidPassword(AuthenticationFlowContext context, UserModel user) {
+        context.getEvent().user(user);
+        context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+        Response challengeResponse = invalidCredentials(context);
+        context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
+        context.clearUser();
+    }
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
index 70d9fd9..7463638 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
@@ -38,7 +38,7 @@ public class UsernamePasswordForm extends AbstractUsernameFormAuthenticator impl
     }
 
     protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
-        return validateUser(context, formData) && validatePassword(context, formData);
+        return validateUserAndPassword(context, formData);
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 71f849b..2bebec3 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -724,7 +724,9 @@ public class AccountService extends AbstractSecuredLocalService {
                         logger.debugv("Social provider {0} removed successfully from user {1}", providerId, user.getUsername());
 
                         event.event(EventType.REMOVE_FEDERATED_IDENTITY).client(auth.getClient()).user(auth.getUser())
-                                .detail(Details.USERNAME, link.getUserId() + "@" + link.getIdentityProvider())
+                                .detail(Details.USERNAME, auth.getUser().getUsername())
+                                .detail(Details.IDENTITY_PROVIDER, link.getIdentityProvider())
+                                .detail(Details.IDENTITY_PROVIDER_USERNAME, link.getUserName())
                                 .success();
 
                         setReferrerOnPage();
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 fda37ef..c8784bd 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -368,6 +368,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
                     context.getUsername(), context.getToken());
             session.users().addFederatedIdentity(realmModel, federatedUser, federatedIdentityModel);
 
+            EventBuilder event = this.event.clone().user(federatedUser)
+                    .detail(Details.CODE_ID, clientSession.getId())
+                    .detail(Details.USERNAME, federatedUser.getUsername())
+                    .detail(Details.IDENTITY_PROVIDER, providerId)
+                    .detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername())
+                    .removeDetail("auth_method");
+
             String isRegisteredNewUser = clientSession.getNote(AbstractIdpAuthenticator.BROKER_REGISTERED_NEW_USER);
             if (Boolean.parseBoolean(isRegisteredNewUser)) {
 
@@ -388,15 +395,17 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
                     federatedUser.setEmailVerified(true);
                 }
 
-                this.event.clone().user(federatedUser).event(EventType.REGISTER)
-                        .detail(Details.IDENTITY_PROVIDER, providerId)
-                        .detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername())
-                        .removeDetail("auth_method")
+                event.event(EventType.REGISTER)
+                        .detail(Details.REGISTER_METHOD, "broker")
+                        .detail(Details.EMAIL, federatedUser.getEmail())
                         .success();
 
             } else {
                 LOGGER.debugf("Linked existing keycloak user '%s' with identity provider '%s' . Identity provider username is '%s' .", federatedUser.getUsername(), providerId, context.getUsername());
 
+                event.event(EventType.FEDERATED_IDENTITY_LINK)
+                        .success();
+
                 updateFederatedIdentity(context, federatedUser);
             }
 
@@ -453,7 +462,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
     }
 
     private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
-        this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING);
+        this.event.event(EventType.FEDERATED_IDENTITY_LINK);
 
         if (federatedUser != null) {
             return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias());
@@ -478,7 +487,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
         this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel);
         context.getIdp().attachUserSession(clientSession.getUserSession(), clientSession, context);
 
-        this.event.success();
+        this.event.user(authenticatedUser)
+                .detail(Details.USERNAME, authenticatedUser.getUsername())
+                .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
+                .detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentityModel.getUserName())
+                .success();
         return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 8f845c0..7f15d2d 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -509,6 +509,10 @@ public class LoginActionsService {
         BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, clientSession);
         AuthenticationFlowModel firstBrokerLoginFlow = realm.getAuthenticationFlowById(brokerContext.getIdpConfig().getFirstBrokerLoginFlowId());
 
+        event.detail(Details.IDENTITY_PROVIDER, brokerContext.getIdpConfig().getAlias())
+                .detail(Details.IDENTITY_PROVIDER_USERNAME, brokerContext.getUsername());
+
+
         AuthenticationProcessor processor = new AuthenticationProcessor() {
 
             @Override
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java
new file mode 100644
index 0000000..7d780cd
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java
@@ -0,0 +1,397 @@
+package org.keycloak.testsuite.broker;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.mail.internet.MimeMessage;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.broker.IdpReviewProfileAuthenticatorFactory;
+import org.keycloak.common.util.ObjectUtil;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorConfigModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
+import org.keycloak.testsuite.pages.IdpLinkEmailPage;
+import org.keycloak.testsuite.pages.LoginPasswordResetPage;
+import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
+import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProviderTest {
+
+    protected static final String APP_REALM_ID = "realm-with-broker";
+
+    @WebResource
+    protected LoginUpdateProfileEditUsernameAllowedPage updateProfileWithUsernamePage;
+
+    @WebResource
+    protected IdpConfirmLinkPage idpConfirmLinkPage;
+
+    @WebResource
+    protected IdpLinkEmailPage idpLinkEmailPage;
+
+    @WebResource
+    protected LoginPasswordUpdatePage passwordUpdatePage;
+
+
+
+    /**
+     * Tests that if updateProfile is off and CreateUserIfUnique authenticator mandatory, error page will be shown if user with same email already exists
+     */
+    @Test
+    public void testErrorPageWhenDuplicationNotAllowed_updateProfileOff() {
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.REQUIRED);
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+            }
+
+        }, APP_REALM_ID);
+
+        loginIDP("pedroigor");
+
+        WebElement element = this.driver.findElement(By.className("instruction"));
+
+        assertNotNull(element);
+
+        assertEquals("User with email psilva@redhat.com already exists. Please login to account management to link the account.", element.getText());
+
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+            }
+
+        }, APP_REALM_ID);
+    }
+
+
+    /**
+     * Tests that if updateProfile is on and CreateUserIfUnique authenticator mandatory, error page will be shown if user with same email already exists
+     */
+    @Test
+    public void testErrorPageWhenDuplicationNotAllowed_updateProfileOn() {
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.REQUIRED);
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_ON);
+            }
+
+        }, APP_REALM_ID);
+
+        loginIDP("test-user");
+
+        this.updateProfileWithUsernamePage.assertCurrent();
+        this.updateProfileWithUsernamePage.update("Test", "User", "test-user@redhat.com", "pedroigor");
+
+        WebElement element = this.driver.findElement(By.className("instruction"));
+
+        assertNotNull(element);
+
+        assertEquals("User with username pedroigor already exists. Please login to account management to link the account.", element.getText());
+
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+            }
+
+        }, APP_REALM_ID);
+    }
+
+
+    /**
+     * Test user registers with IdentityProvider and needs to update password when it's required by IdpCreateUserIfUniqueAuthenticator
+     */
+    @Test
+    public void testRegistrationWithPasswordUpdateRequired() {
+        // Require updatePassword after user registered with broker
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                AuthenticatorConfigModel authenticatorConfig = realmWithBroker.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
+                authenticatorConfig.getConfig().put(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+                realmWithBroker.updateAuthenticatorConfig(authenticatorConfig);
+
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_MISSING);
+            }
+
+        }, APP_REALM_ID);
+
+        loginIDP("pedroigor");
+        this.updateProfileWithUsernamePage.assertCurrent();
+        this.updateProfileWithUsernamePage.update("Test", "User", "some-user@redhat.com", "some-user");
+
+        // Need to update password now
+        this.passwordUpdatePage.assertCurrent();
+        this.passwordUpdatePage.changePassword("password1", "password1");
+
+
+        // assert authenticated
+        assertFederatedUser("some-user", "some-user@redhat.com", "pedroigor");
+
+
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                AuthenticatorConfigModel authenticatorConfig = realmWithBroker.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
+                authenticatorConfig.getConfig().put(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "false");
+                realmWithBroker.updateAuthenticatorConfig(authenticatorConfig);
+            }
+
+        }, APP_REALM_ID);
+    }
+
+
+    /**
+     * Tests that duplication is detected, the confirmation page is displayed, user clicks on "Review profile" and goes back to updateProfile page and resolves duplication
+     * by create new user
+     */
+    @Test
+    public void testFixDuplicationsByReviewProfile() {
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+        loginIDP("pedroigor");
+
+        // There is user with same email. Update profile to use different email
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickReviewProfile();
+
+        this.updateProfileWithUsernamePage.assertCurrent();
+        this.updateProfileWithUsernamePage.update("Test", "User", "testing-user@redhat.com", "pedroigor");
+
+        // There is user with same username. Update profile to use different username
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with username pedroigor already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickReviewProfile();
+
+        this.updateProfileWithUsernamePage.assertCurrent();
+        this.updateProfileWithUsernamePage.update("Test", "User", "testing-user@redhat.com", "testing-user");
+
+        assertFederatedUser("testing-user", "testing-user@redhat.com", "pedroigor");
+    }
+
+    /**
+     * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by email
+     */
+    @Test
+    public void testLinkAccountByEmailVerification() throws Exception {
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+        loginIDP("pedroigor");
+
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickLinkAccount();
+
+        // Confirm linking account by email
+        this.idpLinkEmailPage.assertCurrent();
+        Assert.assertEquals("An email with instructions to link " + ObjectUtil.capitalize(getProviderId()) + " account pedroigor with your " + APP_REALM_ID + " account has been sent to you.", this.idpLinkEmailPage.getMessage());
+
+        Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+        MimeMessage message = greenMail.getReceivedMessages()[0];
+        String linkFromMail = getVerificationEmailLink(message);
+
+        driver.navigate().to(linkFromMail.trim());
+
+        // authenticated and redirected to app. User is linked with identity provider
+        assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+    }
+
+
+    /**
+     * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication (confirm password on login screen)
+     */
+    @Test
+    public void testLinkAccountByReauthenticationWithPassword() throws Exception {
+        // Remove smtp config. The reauthentication by username+password screen will be automatically used then
+        final Map<String, String> smtpConfig = new HashMap<>();
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+                smtpConfig.putAll(realmWithBroker.getSmtpConfig());
+                realmWithBroker.setSmtpConfig(Collections.<String, String>emptyMap());
+            }
+
+        }, APP_REALM_ID);
+
+
+        loginIDP("pedroigor");
+
+
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickLinkAccount();
+
+        // Login screen shown. Username is prefilled and disabled. Registration link and social buttons are not shown
+        Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+        Assert.assertEquals("pedroigor", this.loginPage.getUsername());
+        Assert.assertFalse(this.loginPage.isUsernameInputEnabled());
+
+        Assert.assertEquals("Authenticate as pedroigor to link your account with " + getProviderId(), this.loginPage.getSuccessMessage());
+
+        try {
+            this.loginPage.findSocialButton(getProviderId());
+            Assert.fail("Not expected to see social button with " + getProviderId());
+        } catch (NoSuchElementException expected) {
+        }
+
+        try {
+            this.loginPage.clickRegister();
+            Assert.fail("Not expected to see register link");
+        } catch (NoSuchElementException expected) {
+        }
+
+        // Use bad password first
+        this.loginPage.login("password1");
+        Assert.assertEquals("Invalid username or password.", this.loginPage.getError());
+
+        // Use correct password now
+        this.loginPage.login("password");
+
+        // authenticated and redirected to app. User is linked with identity provider
+        assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+
+
+        // Restore smtp config
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                realmWithBroker.setSmtpConfig(smtpConfig);
+            }
+
+        }, APP_REALM_ID);
+    }
+
+
+    /**
+     * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication (confirm password on login screen)
+     * and additionally he goes through "forget password"
+     */
+    @Test
+    public void testLinkAccountByReauthentication_forgetPassword() throws Exception {
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+                        IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.DISABLED);
+
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+            }
+
+        }, APP_REALM_ID);
+
+        loginIDP("pedroigor");
+
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickLinkAccount();
+
+        // Click "Forget password" on login page. Email sent directly because username is known
+        Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+        this.loginPage.resetPassword();
+
+        Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+        Assert.assertEquals("You should receive an email shortly with further instructions.", this.loginPage.getSuccessMessage());
+
+        // Click on link from email
+        Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+        MimeMessage message = greenMail.getReceivedMessages()[0];
+        String linkFromMail = getVerificationEmailLink(message);
+
+        driver.navigate().to(linkFromMail.trim());
+
+        // Need to update password now
+        this.passwordUpdatePage.assertCurrent();
+        this.passwordUpdatePage.changePassword("password", "password");
+
+        // authenticated and redirected to app. User is linked with identity provider
+        assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+                        IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+
+            }
+
+        }, APP_REALM_ID);
+    }
+
+
+    protected void assertFederatedUser(String expectedUsername, String expectedEmail, String expectedFederatedUsername) {
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+        UserModel federatedUser = getFederatedUser();
+
+        assertNotNull(federatedUser);
+        assertEquals(expectedUsername, federatedUser.getUsername());
+        assertEquals(expectedEmail, federatedUser.getEmail());
+
+        RealmModel realmWithBroker = getRealm();
+        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realmWithBroker);
+        assertEquals(1, federatedIdentities.size());
+
+        FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+        assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+        assertEquals(expectedFederatedUsername, federatedIdentityModel.getUserName());
+    }
+
+
+    protected void setExecutionRequirement(RealmModel realmWithBroker, String flowAlias, String authenticatorProvider, AuthenticationExecutionModel.Requirement requirement) {
+        AuthenticationFlowModel flowModel = realmWithBroker.getFlowByAlias(flowAlias);
+        List<AuthenticationExecutionModel> authExecutions = realmWithBroker.getAuthenticationExecutions(flowModel.getId());
+        for (AuthenticationExecutionModel execution : authExecutions) {
+            if (execution.getAuthenticator().equals(authenticatorProvider)) {
+                execution.setRequirement(requirement);
+                realmWithBroker.updateAuthenticatorExecution(execution);
+                return;
+            }
+        }
+
+        throw new IllegalStateException("Execution not found for flow " + flowAlias + " and authenticator " + authenticatorProvider);
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 8af43b2..5248c38 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -42,6 +42,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.services.Urls;
 import org.keycloak.testsuite.MailUtil;
 import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
 import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
 import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
 import org.keycloak.testsuite.pages.AccountPasswordPage;
@@ -87,7 +88,7 @@ import static org.junit.Assert.fail;
  */
 public abstract class AbstractIdentityProviderTest {
 
-    private static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
+    protected static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
 
     @ClassRule
     public static BrokerKeyCloakRule brokerServerRule = new BrokerKeyCloakRule();
@@ -99,10 +100,10 @@ public abstract class AbstractIdentityProviderTest {
     protected WebDriver driver;
 
     @WebResource
-    private LoginPage loginPage;
+    protected LoginPage loginPage;
 
     @WebResource
-    private LoginUpdateProfilePage updateProfilePage;
+    protected LoginUpdateProfilePage updateProfilePage;
 
 
     @WebResource
@@ -123,7 +124,7 @@ public abstract class AbstractIdentityProviderTest {
     @WebResource
     protected AccountFederatedIdentityPage accountFederatedIdentityPage;
 
-    private KeycloakSession session;
+    protected KeycloakSession session;
 
     @Before
     public void onBefore() {
@@ -140,87 +141,19 @@ public abstract class AbstractIdentityProviderTest {
         brokerServerRule.stopSession(this.session, true);
     }
 
-    @Test
-    public void testSuccessfulAuthentication() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
-
-        UserModel user = assertSuccessfulAuthentication(identityProviderModel, "test-user", "new@email.com", true);
-        Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
-    }
-
-    @Test
-    public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
-
-        assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
-    }
-
-    @Test
-    public void testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
-
-        assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", "new@email.com", true);
-    }
-
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
-        assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
-    }
-
-    /**
-     * Test that verify email action is performed if email is provided and email trust is not enabled for the provider
-     * 
-     * @throws MessagingException
-     * @throws IOException
-     */
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled() throws IOException, MessagingException {
-        getRealm().setVerifyEmail(true);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        try {
-            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-            identityProviderModel.setTrustEmail(false);
-
-            UserModel federatedUser = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "test-user@localhost", false);
-
-            // email is verified now
-            assertFalse(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
-        } finally {
-            getRealm().setVerifyEmail(false);
-        }
-    }
-
-    private UserModel assertSuccessfulAuthenticationWithEmailVerification(IdentityProviderModel identityProviderModel, String username, String expectedEmail,
-            boolean isProfileUpdateExpected)
-            throws IOException, MessagingException {
+    protected UserModel assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel, String username, String expectedEmail, boolean isProfileUpdateExpected) {
         authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
 
-        // verify email is sent
-        Assert.assertTrue(verifyEmailPage.isCurrent());
-
-        // read email to take verification link from
-        Assert.assertEquals(1, greenMail.getReceivedMessages().length);
-        MimeMessage message = greenMail.getReceivedMessages()[0];
-        String verificationUrl = getVerificationEmailLink(message);
-
-        driver.navigate().to(verificationUrl.trim());
-
         // authenticated and redirected to app
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+        assertTrue("Bad current URL " + this.driver.getCurrentUrl() + " and page source: " + this.driver.getPageSource(),
+                this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
 
         UserModel federatedUser = getFederatedUser();
 
         assertNotNull(federatedUser);
+        assertNotNull(federatedUser.getCreatedTimestamp());
+        // test that timestamp is current with 10s tollerance
+        Assert.assertTrue((System.currentTimeMillis() - federatedUser.getCreatedTimestamp()) < 10000);
 
         doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
 
@@ -245,166 +178,7 @@ public abstract class AbstractIdentityProviderTest {
         return federatedUser;
     }
 
-    /**
-     * Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later
-     */
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
-        getRealm().setVerifyEmail(true);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        try {
-            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
-            UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", null, false);
-
-            assertTrue(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
-        } finally {
-            getRealm().setVerifyEmail(false);
-        }
-    }
-
-    /**
-     * Test for KEYCLOAK-1372 - verify email action is not performed if email is provided but email trust is enabled for the provider
-     */
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled() {
-        getRealm().setVerifyEmail(true);
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        try {
-            identityProviderModel.setTrustEmail(true);
-
-            UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
-
-            assertFalse(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
-        } finally {
-            identityProviderModel.setTrustEmail(false);
-            getRealm().setVerifyEmail(false);
-        }
-    }
-
-    /**
-     * Test for KEYCLOAK-1372 - verify email action is performed if email is provided and email trust is enabled for the provider, but email is changed on First login update profile page
-     * 
-     * @throws MessagingException
-     * @throws IOException
-     */
-    @Test
-    public void testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin() throws IOException, MessagingException {
-        getRealm().setVerifyEmail(true);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        try {
-            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
-            identityProviderModel.setTrustEmail(true);
-
-            UserModel user = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "new@email.com", true);
-            Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
-        } finally {
-            identityProviderModel.setTrustEmail(false);
-            getRealm().setVerifyEmail(false);
-        }
-    }
-
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
-
-        getRealm().setRegistrationEmailAsUsername(true);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        try {
-            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
-            authenticateWithIdentityProvider(identityProviderModel, "test-user", false);
-
-            // authenticated and redirected to app
-            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
-            brokerServerRule.stopSession(session, true);
-            session = brokerServerRule.startSession();
-
-            // check correct user is created with email as username and bound to correct federated identity
-            RealmModel realm = getRealm();
-
-            UserModel federatedUser = session.users().getUserByUsername("test-user@localhost", realm);
-
-            assertNotNull(federatedUser);
-
-            assertEquals("test-user@localhost", federatedUser.getUsername());
-
-            doAssertFederatedUser(federatedUser, identityProviderModel, "test-user@localhost", false);
-
-            Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
-            assertEquals(1, federatedIdentities.size());
-
-            FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
-            assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
-
-            driver.navigate().to("http://localhost:8081/test-app/logout");
-            driver.navigate().to("http://localhost:8081/test-app");
-
-            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        } finally {
-            getRealm().setRegistrationEmailAsUsername(false);
-        }
-    }
-
-    @Test
-    public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided() {
-
-        getRealm().setRegistrationEmailAsUsername(true);
-        brokerServerRule.stopSession(this.session, true);
-        this.session = brokerServerRule.startSession();
-
-        try {
-            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
-            authenticateWithIdentityProvider(identityProviderModel, "test-user-noemail", false);
-
-            brokerServerRule.stopSession(session, true);
-            session = brokerServerRule.startSession();
 
-            // check correct user is created with username from provider as email is not available
-            RealmModel realm = getRealm();
-            UserModel federatedUser = getFederatedUser();
-            assertNotNull(federatedUser);
-
-            doAssertFederatedUserNoEmail(federatedUser);
-
-            Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
-            assertEquals(1, federatedIdentities.size());
-
-            FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
-            assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
-            revokeGrant();
-
-            driver.navigate().to("http://localhost:8081/test-app/logout");
-            driver.navigate().to("http://localhost:8081/test-app");
-
-            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        } finally {
-            getRealm().setRegistrationEmailAsUsername(false);
-        }
-    }
 
     protected void doAssertFederatedUserNoEmail(UserModel federatedUser) {
         assertEquals("kc-oidc-idp.test-user-noemail", federatedUser.getUsername());
@@ -413,316 +187,7 @@ public abstract class AbstractIdentityProviderTest {
         assertEquals("User", federatedUser.getLastName());
     }
 
-    @Test
-    public void testDisabled() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
-        identityProviderModel.setEnabled(false);
-
-        this.driver.navigate().to("http://localhost:8081/test-app/");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        try {
-            this.driver.findElement(By.className(getProviderId()));
-            fail("Provider [" + getProviderId() + "] not disabled.");
-        } catch (NoSuchElementException nsee) {
-
-        }
-    }
-
-    @Test
-    public void testProviderOnLoginPage() {
-        // Provider button is available on login page
-        this.driver.navigate().to("http://localhost:8081/test-app/");
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-        loginPage.findSocialButton(getProviderId());
-     }
-
-    // TODO: Reenable and adjust to KEYCLOAK-1750 changed behaviour
-    // @Test
-    public void testUserAlreadyExistsWhenUpdatingProfile() {
-        this.driver.navigate().to("http://localhost:8081/test-app/");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        // choose the identity provider
-        this.loginPage.clickSocial(getProviderId());
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
-
-        // log in to identity provider
-        this.loginPage.login("test-user", "password");
-
-        doAfterProviderAuthentication();
-
-        this.updateProfilePage.assertCurrent();
-        this.updateProfilePage.update("Test", "User", "psilva@redhat.com");
-
-        WebElement element = this.driver.findElement(By.className("kc-feedback-text"));
-
-        assertNotNull(element);
-
-        assertEquals("Email already exists.", element.getText());
-
-        this.updateProfilePage.assertCurrent();
-        this.updateProfilePage.update("Test", "User", "test-user@redhat.com");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
-        UserModel federatedUser = getFederatedUser();
-
-        assertNotNull(federatedUser);
-    }
-
-    // TODO: Reenable and adjust to KEYCLOAK-1750 changed behaviour
-    // @Test
-    public void testUserAlreadyExistsWhenNotUpdatingProfile() {
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
-        this.driver.navigate().to("http://localhost:8081/test-app/");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        // choose the identity provider
-        this.loginPage.clickSocial(getProviderId());
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
-
-        // log in to identity provider
-        this.loginPage.login("pedroigor", "password");
-
-        doAfterProviderAuthentication();
-
-        WebElement element = this.driver.findElement(By.className("kc-feedback-text"));
-
-        assertNotNull(element);
-
-        assertEquals("User with email already exists. Please login to account management to link the account.", element.getText());
-    }
-
-    @Test
-    public void testAccountManagementLinkIdentity() {
-        // Login as pedroigor to account management
-        accountFederatedIdentityPage.realm("realm-with-broker");
-        accountFederatedIdentityPage.open();
-        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
-        loginPage.login("pedroigor", "password");
-        assertTrue(accountFederatedIdentityPage.isCurrent());
-
-        // Link my "pedroigor" identity with "test-user" from brokered Keycloak
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
-        accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getAlias());
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
-        this.loginPage.login("test-user", "password");
-        doAfterProviderAuthentication();
-
-        // Assert identity linked in account management
-        assertTrue(accountFederatedIdentityPage.isCurrent());
-        assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
-
-        // Revoke grant in account mgmt
-        revokeGrant();
-
-        // Logout from account management
-        accountFederatedIdentityPage.logout();
-        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
-
-        // Assert I am logged immediately to account management due to previously linked "test-user" identity
-        loginPage.clickSocial(identityProviderModel.getAlias());
-        doAfterProviderAuthentication();
-        assertTrue(accountFederatedIdentityPage.isCurrent());
-        assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
-
-        // Unlink my "test-user"
-        accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
-        assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
-
-        // Revoke grant in account mgmt
-        revokeGrant();
-
-        // Logout from account management
-        System.out.println("*** logout from account management");
-        accountFederatedIdentityPage.logout();
-        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        // Try to login. Previous link is not valid anymore, so now it should try to register new user
-        this.loginPage.clickSocial(identityProviderModel.getAlias());
-        this.loginPage.login("test-user", "password");
-        doAfterProviderAuthentication();
-        this.updateProfilePage.assertCurrent();
-    }
-
-    @Test(expected = NoSuchElementException.class)
-    public void testIdentityProviderNotAllowed() {
-        this.driver.navigate().to("http://localhost:8081/test-app/");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        driver.findElement(By.className("model-oidc-idp"));
-    }
-
-    protected void configureClientRetrieveToken(String clientId) {
-        RealmModel realm = getRealm();
-        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
-        ClientModel client = realm.getClientByClientId(clientId);
-        if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
-
-        brokerServerRule.stopSession(session, true);
-        session = brokerServerRule.startSession();
-
-    }
-
-    protected void configureUserRetrieveToken(String username) {
-        RealmModel realm = getRealm();
-        UserModel user = session.users().getUserByUsername(username, realm);
-        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
-        if (user != null && !user.hasRole(readTokenRole)) {
-            user.grantRole(readTokenRole);
-        }
-        brokerServerRule.stopSession(session, true);
-        session = brokerServerRule.startSession();
-
-    }
-
-    protected void unconfigureClientRetrieveToken(String clientId) {
-        RealmModel realm = getRealm();
-        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
-        ClientModel client = realm.getClientByClientId(clientId);
-        if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
-
-        brokerServerRule.stopSession(session, true);
-        session = brokerServerRule.startSession();
-
-    }
-
-    protected void unconfigureUserRetrieveToken(String username) {
-        RealmModel realm = getRealm();
-        UserModel user = session.users().getUserByUsername(username, realm);
-        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
-        if (user != null && user.hasRole(readTokenRole)) {
-            user.deleteRoleMapping(readTokenRole);
-        }
-        brokerServerRule.stopSession(session, true);
-        session = brokerServerRule.startSession();
-
-    }
-
-    @Test
-    public void testTokenStorageAndRetrievalByApplication() {
-        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
-        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
-        identityProviderModel.setStoreToken(true);
-
-        authenticateWithIdentityProvider(identityProviderModel, "test-user", true);
-
-        UserModel federatedUser = getFederatedUser();
-        RealmModel realm = getRealm();
-        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
-        assertFalse(federatedIdentities.isEmpty());
-        assertEquals(1, federatedIdentities.size());
-
-        FederatedIdentityModel identityModel = federatedIdentities.iterator().next();
-
-        assertNotNull(identityModel.getToken());
-
-        UserSessionStatus userSessionStatus = retrieveSessionStatus();
-        String accessToken = userSessionStatus.getAccessTokenString();
-        URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
-        final String authHeader = "Bearer " + accessToken;
-        ClientRequestFilter authFilter = new ClientRequestFilter() {
-            @Override
-            public void filter(ClientRequestContext requestContext) throws IOException {
-                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
-            }
-        };
-        Client client = ClientBuilder.newBuilder().register(authFilter).build();
-        WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
-        Response response = tokenEndpoint.request().get();
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        assertNotNull(response.readEntity(String.class));
-        revokeGrant();
-
-
-        driver.navigate().to("http://localhost:8081/test-app/logout");
-        String currentUrl = this.driver.getCurrentUrl();
-        System.out.println("after logout currentUrl: " + currentUrl);
-        assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
-        unconfigureUserRetrieveToken(getProviderId() + ".test-user");
-        loginIDP("test-user");
-        //authenticateWithIdentityProvider(identityProviderModel, "test-user");
-        assertEquals("http://localhost:8081/test-app", driver.getCurrentUrl());
-
-        userSessionStatus = retrieveSessionStatus();
-        accessToken = userSessionStatus.getAccessTokenString();
-        final String authHeader2 = "Bearer " + accessToken;
-        ClientRequestFilter authFilter2 = new ClientRequestFilter() {
-            @Override
-            public void filter(ClientRequestContext requestContext) throws IOException {
-                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader2);
-            }
-        };
-        client = ClientBuilder.newBuilder().register(authFilter2).build();
-        tokenEndpoint = client.target(tokenEndpointUrl);
-        response = tokenEndpoint.request().get();
-
-        assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
-
-        revokeGrant();
-        driver.navigate().to("http://localhost:8081/test-app/logout");
-        driver.navigate().to("http://localhost:8081/test-app");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-    }
-
-    protected abstract void doAssertTokenRetrieval(String pageSource);
-
-    private UserModel assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel, String username, String expectedEmail, boolean isProfileUpdateExpected) {
-        authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
-
-        // authenticated and redirected to app
-        assertTrue("Bad current URL " + this.driver.getCurrentUrl() + " and page source: " + this.driver.getPageSource(),
-                this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
-        UserModel federatedUser = getFederatedUser();
-
-        assertNotNull(federatedUser);
-        assertNotNull(federatedUser.getCreatedTimestamp());
-        // test that timestamp is current with 10s tollerance
-        Assert.assertTrue((System.currentTimeMillis() - federatedUser.getCreatedTimestamp()) < 10000);
-
-        doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
-
-        brokerServerRule.stopSession(session, true);
-        session = brokerServerRule.startSession();
-
-        RealmModel realm = getRealm();
-
-        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
-        assertEquals(1, federatedIdentities.size());
-
-        FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
-        assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
-        assertEquals(federatedUser.getUsername(), federatedIdentityModel.getIdentityProvider() + "." + federatedIdentityModel.getUserName());
-
-        driver.navigate().to("http://localhost:8081/test-app/logout");
-        driver.navigate().to("http://localhost:8081/test-app");
-
-        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-        return federatedUser;
-    }
-
-    private void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel, String username, boolean isProfileUpdateExpected) {
+    protected void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel, String username, boolean isProfileUpdateExpected) {
         loginIDP(username);
 
 
@@ -738,7 +203,7 @@ public abstract class AbstractIdentityProviderTest {
 
     }
 
-    private void loginIDP(String username) {
+    protected void loginIDP(String username) {
         driver.navigate().to("http://localhost:8081/test-app");
 
         assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
@@ -776,6 +241,7 @@ public abstract class AbstractIdentityProviderTest {
 
     protected abstract String getProviderId();
 
+
     protected IdentityProviderModel getIdentityProviderModel() {
         IdentityProviderModel identityProviderModel = getRealm().getIdentityProviderByAlias(getProviderId());
 
@@ -786,10 +252,16 @@ public abstract class AbstractIdentityProviderTest {
         return identityProviderModel;
     }
 
-    private RealmModel getRealm() {
-        return this.session.realms().getRealm("realm-with-broker");
+
+    protected RealmModel getRealm() {
+        return getRealm(this.session);
+    }
+
+    protected RealmModel getRealm(KeycloakSession session) {
+        return session.realms().getRealm("realm-with-broker");
     }
 
+
     protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel, String expectedEmail, boolean isProfileUpdateExpected) {
         if (isProfileUpdateExpected) {
             String userFirstName = "New first";
@@ -805,19 +277,6 @@ public abstract class AbstractIdentityProviderTest {
         }
     }
 
-    private UserSessionStatus retrieveSessionStatus() {
-        UserSessionStatus sessionStatus = null;
-
-        try {
-            String pageSource = this.driver.getPageSource();
-
-            sessionStatus = JsonSerialization.readValue(pageSource.getBytes(), UserSessionStatus.class);
-        } catch (IOException ignore) {
-            ignore.printStackTrace();
-        }
-
-        return sessionStatus;
-    }
 
     private void removeTestUsers() {
         RealmModel realm = getRealm();
@@ -835,40 +294,60 @@ public abstract class AbstractIdentityProviderTest {
             }
         }
     }
-    
-    private String getVerificationEmailLink(MimeMessage message) throws IOException, MessagingException {
-    	Multipart multipart = (Multipart) message.getContent();
-    	
+
+
+    protected void setUpdateProfileFirstLogin(final String updateProfileFirstLogin) {
+        KeycloakModelUtils.runJobInTransaction(this.session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
+
+            @Override
+            public void run(KeycloakSession session) {
+                RealmModel realm = getRealm(session);
+                setUpdateProfileFirstLogin(realm, updateProfileFirstLogin);
+            }
+
+        });
+    }
+
+    protected void setUpdateProfileFirstLogin(RealmModel realm, String updateProfileFirstLogin) {
+        AuthenticatorConfigModel reviewProfileConfig = realm.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS);
+        reviewProfileConfig.getConfig().put(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN, updateProfileFirstLogin);
+        realm.updateAuthenticatorConfig(reviewProfileConfig);
+    }
+
+
+    protected UserSessionStatusServlet.UserSessionStatus retrieveSessionStatus() {
+        UserSessionStatusServlet.UserSessionStatus sessionStatus = null;
+
+        try {
+            String pageSource = this.driver.getPageSource();
+
+            sessionStatus = JsonSerialization.readValue(pageSource.getBytes(), UserSessionStatusServlet.UserSessionStatus.class);
+        } catch (IOException ignore) {
+            ignore.printStackTrace();
+        }
+
+        return sessionStatus;
+    }
+
+    protected String getVerificationEmailLink(MimeMessage message) throws IOException, MessagingException {
+        Multipart multipart = (Multipart) message.getContent();
+
         final String textContentType = multipart.getBodyPart(0).getContentType();
-        
+
         assertEquals("text/plain; charset=UTF-8", textContentType);
-        
+
         final String textBody = (String) multipart.getBodyPart(0).getContent();
         final String textVerificationUrl = MailUtil.getLink(textBody);
-    	
+
         final String htmlContentType = multipart.getBodyPart(1).getContentType();
-        
+
         assertEquals("text/html; charset=UTF-8", htmlContentType);
-        
+
         final String htmlBody = (String) multipart.getBodyPart(1).getContent();
         final String htmlVerificationUrl = MailUtil.getLink(htmlBody);
-        
+
         assertEquals(htmlVerificationUrl, textVerificationUrl);
 
         return htmlVerificationUrl;
     }
-
-    private void setUpdateProfileFirstLogin(final String updateProfileFirstLogin) {
-        KeycloakModelUtils.runJobInTransaction(this.session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
-
-            @Override
-            public void run(KeycloakSession session) {
-                RealmModel realm = session.realms().getRealm("realm-with-broker");
-                AuthenticatorConfigModel reviewProfileConfig = realm.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS);
-                reviewProfileConfig.getConfig().put(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN, updateProfileFirstLogin);
-                realm.updateAuthenticatorConfig(reviewProfileConfig);
-            }
-
-        });
-    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
new file mode 100644
index 0000000..904caf6
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
@@ -0,0 +1,519 @@
+package org.keycloak.testsuite.broker;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.internet.MimeMessage;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.Urls;
+import org.keycloak.testsuite.MailUtil;
+import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author pedroigor
+ */
+public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdentityProviderTest {
+
+    @Test
+    public void testSuccessfulAuthentication() {
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+
+        UserModel user = assertSuccessfulAuthentication(identityProviderModel, "test-user", "new@email.com", true);
+        Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
+    }
+
+    @Test
+    public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
+
+        assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+    }
+
+    @Test
+    public void testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail() {
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
+
+        assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", "new@email.com", true);
+    }
+
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile() {
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+        assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+    }
+
+    /**
+     * Test that verify email action is performed if email is provided and email trust is not enabled for the provider
+     *
+     * @throws MessagingException
+     * @throws IOException
+     */
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled() throws IOException, MessagingException {
+        getRealm().setVerifyEmail(true);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        try {
+            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+            identityProviderModel.setTrustEmail(false);
+
+            UserModel federatedUser = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "test-user@localhost", false);
+
+            // email is verified now
+            assertFalse(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+        } finally {
+            getRealm().setVerifyEmail(false);
+        }
+    }
+
+    private UserModel assertSuccessfulAuthenticationWithEmailVerification(IdentityProviderModel identityProviderModel, String username, String expectedEmail,
+                                                                          boolean isProfileUpdateExpected)
+            throws IOException, MessagingException {
+        authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
+
+        // verify email is sent
+        Assert.assertTrue(verifyEmailPage.isCurrent());
+
+        // read email to take verification link from
+        Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+        MimeMessage message = greenMail.getReceivedMessages()[0];
+        String verificationUrl = getVerificationEmailLink(message);
+
+        driver.navigate().to(verificationUrl.trim());
+
+        // authenticated and redirected to app
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+
+        UserModel federatedUser = getFederatedUser();
+
+        assertNotNull(federatedUser);
+
+        doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
+
+        brokerServerRule.stopSession(session, true);
+        session = brokerServerRule.startSession();
+
+        RealmModel realm = getRealm();
+
+        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+        assertEquals(1, federatedIdentities.size());
+
+        FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+        assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+        assertEquals(federatedUser.getUsername(), federatedIdentityModel.getIdentityProvider() + "." + federatedIdentityModel.getUserName());
+
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+        driver.navigate().to("http://localhost:8081/test-app");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+        return federatedUser;
+    }
+
+    /**
+     * Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later
+     */
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
+        getRealm().setVerifyEmail(true);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        try {
+            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+            UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", null, false);
+
+            assertTrue(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+        } finally {
+            getRealm().setVerifyEmail(false);
+        }
+    }
+
+    /**
+     * Test for KEYCLOAK-1372 - verify email action is not performed if email is provided but email trust is enabled for the provider
+     */
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled() {
+        getRealm().setVerifyEmail(true);
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        try {
+            identityProviderModel.setTrustEmail(true);
+
+            UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+
+            assertFalse(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+        } finally {
+            identityProviderModel.setTrustEmail(false);
+            getRealm().setVerifyEmail(false);
+        }
+    }
+
+    /**
+     * Test for KEYCLOAK-1372 - verify email action is performed if email is provided and email trust is enabled for the provider, but email is changed on First login update profile page
+     *
+     * @throws MessagingException
+     * @throws IOException
+     */
+    @Test
+    public void testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin() throws IOException, MessagingException {
+        getRealm().setVerifyEmail(true);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        try {
+            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+            identityProviderModel.setTrustEmail(true);
+
+            UserModel user = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "new@email.com", true);
+            Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
+        } finally {
+            identityProviderModel.setTrustEmail(false);
+            getRealm().setVerifyEmail(false);
+        }
+    }
+
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
+
+        getRealm().setRegistrationEmailAsUsername(true);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        try {
+            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+            authenticateWithIdentityProvider(identityProviderModel, "test-user", false);
+
+            // authenticated and redirected to app
+            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+
+            brokerServerRule.stopSession(session, true);
+            session = brokerServerRule.startSession();
+
+            // check correct user is created with email as username and bound to correct federated identity
+            RealmModel realm = getRealm();
+
+            UserModel federatedUser = session.users().getUserByUsername("test-user@localhost", realm);
+
+            assertNotNull(federatedUser);
+
+            assertEquals("test-user@localhost", federatedUser.getUsername());
+
+            doAssertFederatedUser(federatedUser, identityProviderModel, "test-user@localhost", false);
+
+            Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+            assertEquals(1, federatedIdentities.size());
+
+            FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+            assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+
+            driver.navigate().to("http://localhost:8081/test-app/logout");
+            driver.navigate().to("http://localhost:8081/test-app");
+
+            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        } finally {
+            getRealm().setRegistrationEmailAsUsername(false);
+        }
+    }
+
+    @Test
+    public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided() {
+
+        getRealm().setRegistrationEmailAsUsername(true);
+        brokerServerRule.stopSession(this.session, true);
+        this.session = brokerServerRule.startSession();
+
+        try {
+            IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+            setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+            authenticateWithIdentityProvider(identityProviderModel, "test-user-noemail", false);
+
+            brokerServerRule.stopSession(session, true);
+            session = brokerServerRule.startSession();
+
+            // check correct user is created with username from provider as email is not available
+            RealmModel realm = getRealm();
+            UserModel federatedUser = getFederatedUser();
+            assertNotNull(federatedUser);
+
+            doAssertFederatedUserNoEmail(federatedUser);
+
+            Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+            assertEquals(1, federatedIdentities.size());
+
+            FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+            assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+            revokeGrant();
+
+            driver.navigate().to("http://localhost:8081/test-app/logout");
+            driver.navigate().to("http://localhost:8081/test-app");
+
+            assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        } finally {
+            getRealm().setRegistrationEmailAsUsername(false);
+        }
+    }
+
+    @Test
+    public void testDisabled() {
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+
+        identityProviderModel.setEnabled(false);
+
+        this.driver.navigate().to("http://localhost:8081/test-app/");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        try {
+            this.driver.findElement(By.className(getProviderId()));
+            fail("Provider [" + getProviderId() + "] not disabled.");
+        } catch (NoSuchElementException nsee) {
+
+        }
+    }
+
+    @Test
+    public void testProviderOnLoginPage() {
+        // Provider button is available on login page
+        this.driver.navigate().to("http://localhost:8081/test-app/");
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+        loginPage.findSocialButton(getProviderId());
+    }
+
+    @Test
+    public void testAccountManagementLinkIdentity() {
+        // Login as pedroigor to account management
+        accountFederatedIdentityPage.realm("realm-with-broker");
+        accountFederatedIdentityPage.open();
+        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+        loginPage.login("pedroigor", "password");
+        assertTrue(accountFederatedIdentityPage.isCurrent());
+
+        // Link my "pedroigor" identity with "test-user" from brokered Keycloak
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+        accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getAlias());
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+        this.loginPage.login("test-user", "password");
+        doAfterProviderAuthentication();
+
+        // Assert identity linked in account management
+        assertTrue(accountFederatedIdentityPage.isCurrent());
+        assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
+
+        // Revoke grant in account mgmt
+        revokeGrant();
+
+        // Logout from account management
+        accountFederatedIdentityPage.logout();
+        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+
+        // Assert I am logged immediately to account management due to previously linked "test-user" identity
+        loginPage.clickSocial(identityProviderModel.getAlias());
+        doAfterProviderAuthentication();
+        assertTrue(accountFederatedIdentityPage.isCurrent());
+        assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
+
+        // Unlink my "test-user"
+        accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
+        assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
+
+        // Revoke grant in account mgmt
+        revokeGrant();
+
+        // Logout from account management
+        System.out.println("*** logout from account management");
+        accountFederatedIdentityPage.logout();
+        assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        // Try to login. Previous link is not valid anymore, so now it should try to register new user
+        this.loginPage.clickSocial(identityProviderModel.getAlias());
+        this.loginPage.login("test-user", "password");
+        doAfterProviderAuthentication();
+        this.updateProfilePage.assertCurrent();
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void testIdentityProviderNotAllowed() {
+        this.driver.navigate().to("http://localhost:8081/test-app/");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        driver.findElement(By.className("model-oidc-idp"));
+    }
+
+    protected void configureClientRetrieveToken(String clientId) {
+        RealmModel realm = getRealm();
+        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+        ClientModel client = realm.getClientByClientId(clientId);
+        if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
+
+        brokerServerRule.stopSession(session, true);
+        session = brokerServerRule.startSession();
+
+    }
+
+    protected void configureUserRetrieveToken(String username) {
+        RealmModel realm = getRealm();
+        UserModel user = session.users().getUserByUsername(username, realm);
+        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+        if (user != null && !user.hasRole(readTokenRole)) {
+            user.grantRole(readTokenRole);
+        }
+        brokerServerRule.stopSession(session, true);
+        session = brokerServerRule.startSession();
+
+    }
+
+    protected void unconfigureClientRetrieveToken(String clientId) {
+        RealmModel realm = getRealm();
+        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+        ClientModel client = realm.getClientByClientId(clientId);
+        if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
+
+        brokerServerRule.stopSession(session, true);
+        session = brokerServerRule.startSession();
+
+    }
+
+    protected void unconfigureUserRetrieveToken(String username) {
+        RealmModel realm = getRealm();
+        UserModel user = session.users().getUserByUsername(username, realm);
+        RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+        if (user != null && user.hasRole(readTokenRole)) {
+            user.deleteRoleMapping(readTokenRole);
+        }
+        brokerServerRule.stopSession(session, true);
+        session = brokerServerRule.startSession();
+
+    }
+
+    @Test
+    public void testTokenStorageAndRetrievalByApplication() {
+        setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+        IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+
+        identityProviderModel.setStoreToken(true);
+
+        authenticateWithIdentityProvider(identityProviderModel, "test-user", true);
+
+        UserModel federatedUser = getFederatedUser();
+        RealmModel realm = getRealm();
+        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+        assertFalse(federatedIdentities.isEmpty());
+        assertEquals(1, federatedIdentities.size());
+
+        FederatedIdentityModel identityModel = federatedIdentities.iterator().next();
+
+        assertNotNull(identityModel.getToken());
+
+        UserSessionStatusServlet.UserSessionStatus userSessionStatus = retrieveSessionStatus();
+        String accessToken = userSessionStatus.getAccessTokenString();
+        URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
+        final String authHeader = "Bearer " + accessToken;
+        ClientRequestFilter authFilter = new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+            }
+        };
+        Client client = ClientBuilder.newBuilder().register(authFilter).build();
+        WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
+        Response response = tokenEndpoint.request().get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertNotNull(response.readEntity(String.class));
+        revokeGrant();
+
+
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+        String currentUrl = this.driver.getCurrentUrl();
+        System.out.println("after logout currentUrl: " + currentUrl);
+        assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+        unconfigureUserRetrieveToken(getProviderId() + ".test-user");
+        loginIDP("test-user");
+        //authenticateWithIdentityProvider(identityProviderModel, "test-user");
+        assertEquals("http://localhost:8081/test-app", driver.getCurrentUrl());
+
+        userSessionStatus = retrieveSessionStatus();
+        accessToken = userSessionStatus.getAccessTokenString();
+        final String authHeader2 = "Bearer " + accessToken;
+        ClientRequestFilter authFilter2 = new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader2);
+            }
+        };
+        client = ClientBuilder.newBuilder().register(authFilter2).build();
+        tokenEndpoint = client.target(tokenEndpointUrl);
+        response = tokenEndpoint.request().get();
+
+        assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
+
+        revokeGrant();
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+        driver.navigate().to("http://localhost:8081/test-app");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+    }
+
+    protected abstract void doAssertTokenRetrieval(String pageSource);
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java
new file mode 100644
index 0000000..d689363
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java
@@ -0,0 +1,145 @@
+package org.keycloak.testsuite.broker;
+
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.openqa.selenium.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
+
+    private static final int PORT = 8082;
+
+    @ClassRule
+    public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+        @Override
+        protected void configureServer(KeycloakServer server) {
+            server.getConfig().setPort(PORT);
+        }
+
+        @Override
+        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+            server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
+            server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml.json"));
+        }
+
+        @Override
+        protected String[] getTestRealms() {
+            return new String[] { "realm-with-oidc-identity-provider", "realm-with-saml-idp-basic" };
+        }
+    };
+
+    @Override
+    protected String getProviderId() {
+        return "kc-oidc-idp";
+    }
+
+
+    /**
+     * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication
+     * with different broker already linked to his account
+     */
+    @Test
+    public void testLinkAccountByReauthenticationWithDifferentBroker() throws Exception {
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+                        IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.DISABLED);
+
+                setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+            }
+
+        }, APP_REALM_ID);
+
+        // First link "pedroigor" user with SAML broker and logout
+        driver.navigate().to("http://localhost:8081/test-app");
+        this.loginPage.clickSocial("kc-saml-idp-basic");
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+        Assert.assertEquals("Log in to realm-with-saml-idp-basic", this.driver.getTitle());
+        this.loginPage.login("pedroigor", "password");
+
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickLinkAccount();
+
+        this.loginPage.login("password");
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+        driver.navigate().to("http://localhost:8081/test-app/logout");
+
+
+        // login through OIDC broker now
+        loginIDP("pedroigor");
+
+        this.idpConfirmLinkPage.assertCurrent();
+        Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+        this.idpConfirmLinkPage.clickLinkAccount();
+
+        // assert reauthentication with login page. On login page is link to kc-saml-idp-basic as user has it linked already
+        Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+        Assert.assertEquals("Authenticate as pedroigor to link your account with " + getProviderId(), this.loginPage.getSuccessMessage());
+
+        try {
+            this.loginPage.findSocialButton(getProviderId());
+            Assert.fail("Not expected to see social button with " + getProviderId());
+        } catch (NoSuchElementException expected) {
+        }
+
+        // reauthenticate with SAML broker
+        this.loginPage.clickSocial("kc-saml-idp-basic");
+        Assert.assertEquals("Log in to realm-with-saml-idp-basic", this.driver.getTitle());
+        this.loginPage.login("pedroigor", "password");
+
+
+        // authenticated and redirected to app. User is linked with identity provider
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+        UserModel federatedUser = getFederatedUser();
+
+        assertNotNull(federatedUser);
+        assertEquals("pedroigor", federatedUser.getUsername());
+        assertEquals("psilva@redhat.com", federatedUser.getEmail());
+
+        RealmModel realmWithBroker = getRealm();
+        Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realmWithBroker);
+        assertEquals(2, federatedIdentities.size());
+
+        for (FederatedIdentityModel link : federatedIdentities) {
+            Assert.assertEquals("pedroigor", link.getUserName());
+            Assert.assertTrue(link.getIdentityProvider().equals(getProviderId()) || link.getIdentityProvider().equals("kc-saml-idp-basic"));
+        }
+
+        brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+                setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+                        IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+
+            }
+
+        }, APP_REALM_ID);
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
index 0a7ee16..5bfdc1d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.fail;
 /**
  * @author pedroigor
  */
-public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderTest {
+public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityProviderTest {
 
     private static final int PORT = 8082;
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java
new file mode 100644
index 0000000..66692d4
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java
@@ -0,0 +1,40 @@
+package org.keycloak.testsuite.broker;
+
+import org.junit.ClassRule;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SAMLFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
+
+    private static final int PORT = 8082;
+
+    @ClassRule
+    public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+        @Override
+        protected void configureServer(KeycloakServer server) {
+            server.getConfig().setPort(PORT);
+        }
+
+        @Override
+        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+            server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml.json"));
+        }
+
+        @Override
+        protected String[] getTestRealms() {
+            return new String[] { "realm-with-saml-idp-basic" };
+        }
+    };
+
+    @Override
+    protected String getProviderId() {
+        return "kc-saml-idp-basic";
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
index 7bbaa20..8be9dcc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.fail;
 /**
  * @author pedroigor
  */
-public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderTest {
+public class SAMLKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityProviderTest {
 
     @ClassRule
     public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
index 249f2e0..197749b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.fail;
 /**
  * @author pedroigor
  */
-public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityProviderTest {
+public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractKeycloakIdentityProviderTest {
 
     @ClassRule
     public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index 950c182..30d94fa 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -215,9 +215,10 @@ public class LoginTest {
             Assert.assertEquals("login-test", loginPage.getUsername());
             Assert.assertEquals("", loginPage.getPassword());
 
-            Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError());
+            // KEYCLOAK-2024
+            Assert.assertEquals("Invalid username or password.", loginPage.getError());
 
-            events.expectLogin().user(userId).session((String) null).error("user_disabled")
+            events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials")
                     .detail(Details.USERNAME, "login-test")
                     .removeDetail(Details.CONSENT)
                     .assertEvent();
@@ -250,6 +251,7 @@ public class LoginTest {
             Assert.assertEquals("login-test", loginPage.getUsername());
             Assert.assertEquals("", loginPage.getPassword());
 
+            // KEYCLOAK-2024
             Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError());
 
             events.expectLogin().user(userId).session((String) null).error("user_disabled")
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
new file mode 100644
index 0000000..83596a0
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
@@ -0,0 +1,41 @@
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpConfirmLinkPage extends AbstractPage {
+
+    @FindBy(id = "updateProfile")
+    private WebElement updateProfileButton;
+
+    @FindBy(id = "linkAccount")
+    private WebElement linkAccountButton;
+
+    @FindBy(className = "instruction")
+    private WebElement message;
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().equals("Account already exists");
+    }
+
+    public String getMessage() {
+        return message.getText();
+    }
+
+    public void clickReviewProfile() {
+        updateProfileButton.click();
+    }
+
+    public void clickLinkAccount() {
+        linkAccountButton.click();
+    }
+
+    @Override
+    public void open() throws Exception {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
new file mode 100644
index 0000000..91387b5
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpLinkEmailPage extends AbstractPage {
+
+    @FindBy(id = "instruction1")
+    private WebElement message;
+
+    @Override
+    public boolean isCurrent() {
+        return driver.getTitle().startsWith("Link ");
+    }
+
+    @Override
+    public void open() throws Exception {
+        throw new UnsupportedOperationException();
+    }
+
+    public String getMessage() {
+        return message.getText();
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index f329533..04e5ddd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -112,6 +112,10 @@ public class LoginPage extends AbstractPage {
         return usernameInput.getAttribute("value");
     }
 
+    public boolean isUsernameInputEnabled() {
+        return usernameInput.isEnabled();
+    }
+
     public String getPassword() {
         return passwordInput.getAttribute("value");
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
index d67862c..c9038a4 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
@@ -21,6 +21,8 @@
  */
 package org.keycloak.testsuite.pages;
 
+import org.junit.Assert;
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;