keycloak-aplcache

Changes

services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java 495(+0 -495)

Details

diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
index 8f81a6f..761decc 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
@@ -31,6 +31,7 @@ public class FederatedIdentity {
     private String lastName;
     private String email;
     private String token;
+    private String identityProviderId;
 
     public FederatedIdentity(String id) {
         if (id == null) {
@@ -92,4 +93,25 @@ public class FederatedIdentity {
     public String getToken() {
         return this.token;
     }
+
+    public String getIdentityProviderId() {
+        return this.identityProviderId;
+    }
+
+    public void setIdentityProviderId(String identityProviderId) {
+        this.identityProviderId = identityProviderId;
+    }
+
+    @Override
+    public String toString() {
+        return "{" +
+                "id='" + id + '\'' +
+                ", username='" + username + '\'' +
+                ", firstName='" + firstName + '\'' +
+                ", lastName='" + lastName + '\'' +
+                ", email='" + email + '\'' +
+                ", token='" + token + '\'' +
+                ", identityProviderId='" + identityProviderId + '\'' +
+                '}';
+    }
 }
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java
new file mode 100644
index 0000000..858c820
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+/**
+ * @author pedroigor
+ */
+public class IdentityBrokerException extends RuntimeException {
+
+    public IdentityBrokerException(String message) {
+        super(message);
+    }
+
+    public IdentityBrokerException(String message, Throwable t) {
+        super(message, t);
+    }
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
index f84765a..ac1e3dc 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -68,5 +68,12 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
      */
     AuthenticationResponse handleResponse(AuthenticationRequest request);
 
+    /**
+     * <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
+     * specific user.</p>
+     *
+     * @param identity
+     * @return
+     */
     Response retrieveToken(FederatedIdentityModel identity);
 }
diff --git a/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java b/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
index 56b30fd..84ceb22 100644
--- a/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
+++ b/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
@@ -103,15 +103,23 @@ public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosI
             logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader);
         }
 
-        // Error page is rendered just if browser is unable to send Authorization header with SPNEGO token
-        Response response = request.getSession().getProvider(LoginFormsProvider.class)
+        Response response;
+        LoginFormsProvider loginFormsProvider = request.getSession().getProvider(LoginFormsProvider.class)
                 .setRealm(request.getRealm())
                 .setUriInfo(request.getUriInfo())
-                .setClient(request.getClientSession().getClient())
-                .setClientSessionCode(getRelayState(request))
-                .setWarning("errorKerberosLogin")
-                .setStatus(Response.Status.UNAUTHORIZED)
-                .createLogin();
+                .setStatus(Response.Status.UNAUTHORIZED);
+
+        if (request.getClientSession().getUserSession() == null) {
+            // User not logged. Display HTML with login form as fallback if SPNEGO token not found
+            response = loginFormsProvider.setClient(request.getClientSession().getClient())
+                    .setClientSessionCode(getRelayState(request))
+                    .setWarning("errorKerberosLogin")
+                    .createLogin();
+        } else {
+            // User logged and linking account. Display HTML with error if SPNEGO token not found
+            response = loginFormsProvider.setError("errorKerberosLinkAccount")
+                    .createErrorPage();
+        }
 
         response.getMetadata().putSingle(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader);
         return AuthenticationResponse.fromResponse(response);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index 14bc767..34075e9 100644
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -25,6 +25,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.AuthenticationResponse;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.models.FederatedIdentityModel;
 
 import javax.ws.rs.core.Response;
@@ -68,7 +69,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
 
             return AuthenticationResponse.temporaryRedirect(authorizationUrl);
         } catch (Exception e) {
-            throw new RuntimeException("Could not create authentication request.", e);
+            throw new IdentityBrokerException("Could not create authentication request.", e);
         }
     }
 
@@ -85,9 +86,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
 
         if (error != null) {
             if (error.equals("access_denied")) {
-                throw new RuntimeException("Access denied.");
+                throw new IdentityBrokerException("Access denied.");
             } else {
-                throw new RuntimeException(error);
+                throw new IdentityBrokerException(error);
             }
         }
 
@@ -111,9 +112,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
                 return AuthenticationResponse.end(federatedIdentity);
             }
 
-            throw new RuntimeException("No authorization code from identity provider.");
+            throw new IdentityBrokerException("No authorization code from identity provider.");
         } catch (Exception e) {
-            throw new RuntimeException("Could not process response from identity provider.", e);
+            throw new IdentityBrokerException("Could not process response from identity provider.", e);
         }
     }
 
@@ -132,7 +133,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
             try {
                 return mapper.readTree(response).get(tokenName).getTextValue();
             } catch (IOException e) {
-                throw new RuntimeException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
+                throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
             }
         } else {
             Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
@@ -149,7 +150,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
         String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
 
         if (accessToken == null) {
-            throw new RuntimeException("No access token from server.");
+            throw new IdentityBrokerException("No access token from server.");
         }
 
         return doGetFederatedIdentity(accessToken);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 50a5f03..2c43d61 100644
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -21,6 +21,7 @@ import org.codehaus.jackson.JsonNode;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.jose.jws.JWSInput;
 
 import javax.ws.rs.core.UriBuilder;
@@ -62,7 +63,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
         String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
 
         if (accessToken == null) {
-            throw new RuntimeException("No access_token from server.");
+            throw new IdentityBrokerException("No access_token from server.");
         }
 
         String idToken = extractTokenFromResponse(response, OIDC_PARAMETER_ID_TOKEN);
@@ -101,13 +102,13 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
 
             return identity;
         } catch (Exception e) {
-            throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e);
+            throw new IdentityBrokerException("Could not fetch attributes from userinfo endpoint.", e);
         }
     }
 
     private void validateIdToken(String idToken) {
         if (idToken == null) {
-            throw new RuntimeException("No id_token from server.");
+            throw new IdentityBrokerException("No id_token from server.");
         }
 
         try {
@@ -131,10 +132,10 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
                     }
                 }
 
-                throw new RuntimeException("Wrong issuer from id_token..");
+                throw new IdentityBrokerException("Wrong issuer from id_token..");
             }
         } catch (IOException e) {
-            throw new RuntimeException("Could not decode id token.", e);
+            throw new IdentityBrokerException("Could not decode id token.", e);
         }
     }
 
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 8e458bc..1ad696c 100644
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -22,6 +22,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.AuthenticationResponse;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
 import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
@@ -112,11 +113,11 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
                 PublicKey publicKey = request.getRealm().getPublicKey();
 
                 if (privateKey == null) {
-                    throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a private key.");
+                    throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a private key.");
                 }
 
                 if (publicKey == null) {
-                    throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a public key.");
+                    throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a public key.");
                 }
 
                 KeyPair keypair = new KeyPair(publicKey, privateKey);
@@ -131,7 +132,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
                 return AuthenticationResponse.fromResponse(authnRequestBuilder.redirectBinding().request());
             }
         } catch (Exception e) {
-            throw new RuntimeException("Could not create authentication request.", e);
+            throw new IdentityBrokerException("Could not create authentication request.", e);
         }
     }
 
@@ -145,7 +146,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
         String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
 
         if (samlResponse == null) {
-            throw new RuntimeException("No response from SAML identity provider.");
+            throw new IdentityBrokerException("No response from SAML identity provider.");
         }
 
         try {
@@ -167,7 +168,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
             return AuthenticationResponse.end(identity);
         } catch (Exception e) {
-            throw new RuntimeException("Could not process response from SAML identity provider.", e);
+            throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
         }
     }
 
@@ -194,7 +195,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
         List<RTChoiceType> assertions = responseType.getAssertions();
 
         if (assertions.isEmpty()) {
-            throw new RuntimeException("No assertion from response.");
+            throw new IdentityBrokerException("No assertion from response.");
         }
 
         RTChoiceType rtChoiceType = assertions.get(0);
@@ -234,7 +235,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
                 detailMessage.append("none");
             }
 
-            throw new RuntimeException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
+            throw new IdentityBrokerException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
         }
     }
 
@@ -246,7 +247,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
             Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
 
             if (enc == null) {
-                throw new RuntimeException("No encrypted assertion found.");
+                throw new IdentityBrokerException("No encrypted assertion found.");
             }
 
             String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
@@ -265,7 +266,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
 
             return responseType;
         } catch (Exception e) {
-            throw new RuntimeException("Could not decrypt assertion.", e);
+            throw new IdentityBrokerException("Could not decrypt assertion.", e);
         }
     }
 
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
index a62fb92..f48615b 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -43,6 +43,7 @@
             <column name="PROVIDER_ID" type="VARCHAR(255)"/>
             <column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
             <column name="STORE_TOKEN" type="BOOLEAN(1)"/>
+            <column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN(1)"/>
             <column name="REALM_ID" type="VARCHAR(36)"/>
         </createTable>
         <createTable tableName="IDENTITY_PROVIDER_CONFIG">
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
index a24253c..83d8cc6 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
@@ -8,6 +8,7 @@ import com.mongodb.BasicDBObjectBuilder;
 import com.mongodb.DBCollection;
 import com.mongodb.DBCursor;
 import com.mongodb.DBObject;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -52,11 +53,14 @@ public class Update1_2_0_Beta1 extends Update {
                                 .add("clientSecret", clientSecret).get();
 
                         DBObject identityProvider = new BasicDBObjectBuilder()
+                                .add("internalId", KeycloakModelUtils.generateId())
                                 .add("providerId", socialProviderId)
                                 .add("name", socialProviderId)
                                 .add("id", socialProviderId)
                                 .add("updateProfileFirstLogin", updateProfileOnInitialSocialLogin)
                                 .add("enabled", true)
+                                .add("storeToken", false)
+                                .add("authenticateByDefault", false)
                                 .add("config", identityProviderConfig).get();
 
                         identityProviders.add(identityProvider);
diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
index b9f09c3..c0b0852 100644
--- a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
@@ -32,6 +32,7 @@ public class IdentityProviderRepresentation {
     protected boolean enabled = true;
     protected boolean updateProfileFirstLogin = true;
     protected boolean storeToken;
+    protected boolean authenticateByDefault;
     protected String groupName;
     protected Map<String, String> config = new HashMap<String, String>();
 
@@ -91,6 +92,14 @@ public class IdentityProviderRepresentation {
         this.updateProfileFirstLogin = updateProfileFirstLogin;
     }
 
+    public boolean isAuthenticateByDefault() {
+        return authenticateByDefault;
+    }
+
+    public void setAuthenticateByDefault(boolean authenticateByDefault) {
+        this.authenticateByDefault = authenticateByDefault;
+    }
+
     public boolean isStoreToken() {
         return this.storeToken;
     }
diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java
index 1ca58ec..fa1e57a 100755
--- a/events/api/src/main/java/org/keycloak/events/Details.java
+++ b/events/api/src/main/java/org/keycloak/events/Details.java
@@ -12,6 +12,8 @@ public interface Details {
     String REDIRECT_URI = "redirect_uri";
     String RESPONSE_TYPE = "response_type";
     String AUTH_METHOD = "auth_method";
+    String IDENTITY_PROVIDER = "identity_provider";
+    String IDENTITY_PROVIDER_IDENTITY = "identity_provider_identity";
     String REGISTER_METHOD = "register_method";
     String USERNAME = "username";
     String REMEMBER_ME = "remember_me";
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 2fa691e..94e5a7e 100755
--- a/events/api/src/main/java/org/keycloak/events/Errors.java
+++ b/events/api/src/main/java/org/keycloak/events/Errors.java
@@ -35,7 +35,6 @@ public interface Errors {
 
     String NOT_ALLOWED = "not_allowed";
 
-    String IDENTITY_PROVIDER_NOT_FOUND = "identity_provider_not_found";
     String FEDERATED_IDENTITY_EMAIL_EXISTS = "federated_identity_email_exists";
     String FEDERATED_IDENTITY_USERNAME_EXISTS = "federated_identity_username_exists";
     String FEDERATED_IDENTITY_DISABLED_REGISTRATION = "federated_identity_disabled_registration";
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 d292c4c..d66660d 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -48,5 +48,14 @@ public enum EventType {
     UNREGISTER_NODE,
 
     USER_INFO_REQUEST,
-    USER_INFO_REQUEST_ERROR
+    USER_INFO_REQUEST_ERROR,
+
+    IDENTITY_PROVIDER_LOGIN,
+    IDENTITY_PROVIDER_LOGIN_ERROR,
+    IDENTITY_PROVIDER_RESPONSE,
+    IDENTITY_PROVIDER_RESPONSE_ERROR,
+    IDENTITY_PROVIDER_RETRIEVE_TOKEN,
+    IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR,
+    IDENTITY_PROVIDER_ACCCOUNT_LINKING,
+    IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR,
 }
diff --git a/examples/broker/facebook-authentication/src/main/webapp/js/app.js b/examples/broker/facebook-authentication/src/main/webapp/js/app.js
index 14999a7..07ba177 100755
--- a/examples/broker/facebook-authentication/src/main/webapp/js/app.js
+++ b/examples/broker/facebook-authentication/src/main/webapp/js/app.js
@@ -83,7 +83,7 @@ module.controller('GlobalCtrl', function($scope, $http, $location, Auth) {
     $scope.identity = Auth.getIdentity();
 
     $scope.loadSocialProfile = function() {
-        $http.get('http://localhost:8081/auth/broker/facebook-identity-provider-realm/facebook/token').success(function(data) {
+        $http.get('http://localhost:8081/auth/realms/facebook-identity-provider-realm/broker/facebook/token').success(function(data) {
             var accessTokenParameter = 'access_token=';
             var accessToken = data.substring(data.indexOf(accessTokenParameter) + accessTokenParameter.length, data.indexOf('&'));
 
diff --git a/examples/broker/google-authentication/src/main/webapp/js/app.js b/examples/broker/google-authentication/src/main/webapp/js/app.js
index 1662f35..a7e363c 100755
--- a/examples/broker/google-authentication/src/main/webapp/js/app.js
+++ b/examples/broker/google-authentication/src/main/webapp/js/app.js
@@ -83,7 +83,7 @@ module.controller('GlobalCtrl', function($scope, $http, $location, Auth) {
     $scope.identity = Auth.getIdentity();
 
     $scope.loadSocialProfile = function() {
-        $http.get('http://localhost:8081/auth/broker/google-identity-provider-realm/google/token').success(function(data) {
+        $http.get('http://localhost:8081/auth/realms/google-identity-provider-realm/broker/google/token').success(function(data) {
             var accessToken = data.access_token;
 
             var req = {
diff --git a/examples/broker/saml-broker-authentication/saml-broker-realm.json b/examples/broker/saml-broker-authentication/saml-broker-realm.json
index 8c28ba1..016b843 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-realm.json
@@ -31,7 +31,7 @@
             "name": "http://localhost:8080/auth/",
             "enabled": true,
             "redirectUris": [
-              "http://localhost:8080/auth/broker/saml-broker-authentication-realm/saml-identity-provider"
+              "http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
             ],
           "attributes": {
             "saml.assertion.signature": "true",
diff --git a/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java b/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
index 625284b..7fdcb4e 100644
--- a/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
+++ b/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
@@ -114,7 +114,7 @@ public class TwitterShowUserServlet extends HttpServlet {
     }
 
     private String getIdentityProviderTokenUrl() {
-        return this.authServer + "/broker/" + this.realmName + "/" + this.identityProvider.getId() + "/token";
+        return this.authServer + "/realms/" + this.realmName + "/broker/" + this.identityProvider.getId() + "/token";
     }
 
     private void initKeyCloakClient(ServletConfig config) {
diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml
index e8be627..bd10f77 100755
--- a/examples/saml/pom.xml
+++ b/examples/saml/pom.xml
@@ -3,7 +3,7 @@
     <parent>
         <artifactId>examples-pom</artifactId>
         <groupId>org.keycloak</groupId>
-        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <version>1.2.0.Beta1-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <name>Provider Examples</name>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 4971ee1..5c25e1b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -653,6 +653,8 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
         $scope.identityProvider.name = providerFactory.name;
         $scope.identityProvider.enabled = true;
         $scope.identityProvider.updateProfileFirstLogin = true;
+        // Kerberos is suggested as default provider, others not
+        $scope.identityProvider.authenticateByDefault = (providerFactory.id === "kerberos");
         $scope.newIdentityProvider = true;
     }
 
@@ -719,7 +721,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
         }
     }, true);
 
-    $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/broker/") + realm.realm + "/" ;
+    $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/realms/") + realm.realm + "/broker/" ;
 
     $scope.addProvider = function(provider) {
         $location.url("/create/identity-provider/" + realm.realm + "/" + provider.id);
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
index 489628a..b061234 100644
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
@@ -60,6 +60,13 @@
                     </div>
                     <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
                 </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+                    <div class="col-sm-4">
+                        <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+                    </div>
+                    <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+                </div>
             </fieldset>
 
             <div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
index 1e4de59..8680ac1 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
@@ -113,6 +113,13 @@
                         </div>
                         <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
                     </div>
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+                        <div class="col-sm-4">
+                            <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+                        </div>
+                        <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+                    </div>
                 </fieldset>
 
                 <div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
index 91cf006..2f08e48 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
@@ -114,6 +114,13 @@
                         </div>
                         <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
                     </div>
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+                        <div class="col-sm-4">
+                            <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+                        </div>
+                        <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+                    </div>
                 </fieldset>
 
                 <div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
index e5a28e1..3e33cc6 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
@@ -70,6 +70,13 @@
                         </div>
                         <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
                     </div>
+                    <div class="form-group">
+                        <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+                        <div class="col-sm-4">
+                            <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+                        </div>
+                        <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+                    </div>
                 </fieldset>
 
                 <div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
index 3819d67..c7e1fff 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -34,7 +34,7 @@ invalidPassword=Invalid username or password.
 invalidEmail=Invalid email address
 accountDisabled=Account is disabled, contact admin
 accountTemporarilyDisabled=Account is temporarily disabled, contact admin or try again later
-expiredCode=Login timeout or unknown action. Please login again
+expiredCode=Login timeout. Please login again
 
 missingFirstName=Please specify first name
 missingLastName=Please specify last name
@@ -98,7 +98,8 @@ actionPasswordWarning=You need to change your password to activate your account.
 actionEmailWarning=You need to verify your email address to activate your account.
 actionFollow=Please fill in the fields below.
 
-errorKerberosLogin=Unable to login with Kerberos. Request Kerberos ticket or use different login mechanism
+errorKerberosLogin=Kerberos ticket not available. Use different login mechanism
+errorKerberosLinkAccount=Kerberos ticket not available.
 
 successHeader=Success!
 errorHeader=Error!
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
index 5a77a1a..dfa2f71 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
@@ -61,7 +61,7 @@ public class IdentityProviderBean {
                         }
                     }
 
-                    String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider, realm).toString();
+                    String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getId(), realm.getName()).toString();
                     providers.add(new IdentityProvider(identityProvider.getId(), identityProvider.getName(), loginUrl));
                 }
             }
diff --git a/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
index 4fb33f2..3343b90 100644
--- a/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
@@ -32,6 +32,7 @@ public class IdentityProviderEntity {
     private boolean enabled;
     private boolean updateProfileFirstLogin;
     private boolean storeToken;
+    private boolean authenticateByDefault;
 
     private Map<String, String> config = new HashMap<String, String>();
 
@@ -67,6 +68,14 @@ public class IdentityProviderEntity {
         this.updateProfileFirstLogin = updateProfileFirstLogin;
     }
 
+    public boolean isAuthenticateByDefault() {
+        return authenticateByDefault;
+    }
+
+    public void setAuthenticateByDefault(boolean authenticateByDefault) {
+        this.authenticateByDefault = authenticateByDefault;
+    }
+
     public boolean isStoreToken() {
         return this.storeToken;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
index 76ca2d8..ca7063f 100644
--- a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
+++ b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
@@ -53,6 +53,11 @@ public class IdentityProviderModel {
     private boolean storeToken;
 
     /**
+     * Specifies if particular provider should be used by default for authentication even before displaying login screen
+     */
+    private boolean authenticateByDefault;
+
+    /**
      * <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
      * in the map are understood by the identity provider implementation.</p>
      */
@@ -70,6 +75,7 @@ public class IdentityProviderModel {
         this.enabled = model.isEnabled();
         this.updateProfileFirstLogin = model.isUpdateProfileFirstLogin();
         this.storeToken = model.isStoreToken();
+        this.authenticateByDefault = model.isAuthenticateByDefault();
     }
 
     public String getInternalId() {
@@ -128,6 +134,14 @@ public class IdentityProviderModel {
         this.storeToken = storeToken;
     }
 
+    public boolean isAuthenticateByDefault() {
+        return authenticateByDefault;
+    }
+
+    public void setAuthenticateByDefault(boolean authenticateByDefault) {
+        this.authenticateByDefault = authenticateByDefault;
+    }
+
     public Map<String, String> getConfig() {
         return this.config;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 444d704..72b62d2 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -314,6 +314,7 @@ public class ModelToRepresentation {
         providerRep.setEnabled(identityProviderModel.isEnabled());
         providerRep.setStoreToken(identityProviderModel.isStoreToken());
         providerRep.setUpdateProfileFirstLogin(identityProviderModel.isUpdateProfileFirstLogin());
+        providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());
         providerRep.setConfig(identityProviderModel.getConfig());
 
         return providerRep;
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 8463168..8a0fc26 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -759,6 +759,7 @@ public class RepresentationToModel {
         identityProviderModel.setName(representation.getName());
         identityProviderModel.setEnabled(representation.isEnabled());
         identityProviderModel.setUpdateProfileFirstLogin(representation.isUpdateProfileFirstLogin());
+        identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
         identityProviderModel.setStoreToken(representation.isStoreToken());
         identityProviderModel.setConfig(representation.getConfig());
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
index 408d706..b1a9f10 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
@@ -50,6 +50,9 @@ public class IdentityProviderEntity {
     @Column(name="STORE_TOKEN")
     private boolean storeToken;
 
+    @Column(name="AUTHENTICATE_BY_DEFAULT")
+    private boolean authenticateByDefault;
+
     @ElementCollection
     @MapKeyColumn(name="name")
     @Column(name="value", columnDefinition = "TEXT")
@@ -120,6 +123,14 @@ public class IdentityProviderEntity {
         this.storeToken = storeToken;
     }
 
+    public boolean isAuthenticateByDefault() {
+        return authenticateByDefault;
+    }
+
+    public void setAuthenticateByDefault(boolean authenticateByDefault) {
+        this.authenticateByDefault = authenticateByDefault;
+    }
+
     public Map<String, String> getConfig() {
         return this.config;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 97c0a24..bbc885e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1124,6 +1124,7 @@ public class RealmAdapter implements RealmModel {
             identityProviderModel.setConfig(entity.getConfig());
             identityProviderModel.setEnabled(entity.isEnabled());
             identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
+            identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
             identityProviderModel.setStoreToken(entity.isStoreToken());
 
             identityProviders.add(identityProviderModel);
@@ -1154,6 +1155,7 @@ public class RealmAdapter implements RealmModel {
         entity.setEnabled(identityProvider.isEnabled());
         entity.setStoreToken(identityProvider.isStoreToken());
         entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+        entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
         entity.setConfig(identityProvider.getConfig());
 
         realm.addIdentityProvider(entity);
@@ -1180,6 +1182,7 @@ public class RealmAdapter implements RealmModel {
                 entity.setName(identityProvider.getName());
                 entity.setEnabled(identityProvider.isEnabled());
                 entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+                entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
                 entity.setStoreToken(identityProvider.isStoreToken());
                 entity.setConfig(identityProvider.getConfig());
             }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index aa3dcd4..52d4c98 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -251,7 +251,8 @@ public class MongoUserProvider implements UserProvider {
     @Override
     public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
         FederatedIdentityEntity federatedIdentityEntity = findSocialLink(user, socialProvider, realm);
-        return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()) : null;
+        return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(),
+                federatedIdentityEntity.getUserName(), federatedIdentityEntity.getToken()) : null;
     }
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index dafeb22..f24cc02 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -940,6 +940,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
             identityProviderModel.setConfig(entity.getConfig());
             identityProviderModel.setEnabled(entity.isEnabled());
             identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
+            identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
             identityProviderModel.setStoreToken(entity.isStoreToken());
 
             identityProviders.add(identityProviderModel);
@@ -969,6 +970,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setName(identityProvider.getName());
         entity.setEnabled(identityProvider.isEnabled());
         entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+        entity.setStoreToken(identityProvider.isStoreToken());
+        entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
         entity.setConfig(identityProvider.getConfig());
 
         realm.getIdentityProviders().add(entity);
@@ -995,6 +998,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
                 entity.setName(identityProvider.getName());
                 entity.setEnabled(identityProvider.isEnabled());
                 entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+                entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
                 entity.setStoreToken(identityProvider.isStoreToken());
                 entity.setConfig(identityProvider.getConfig());
             }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
index da7d190..fd20b16 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
@@ -877,9 +877,7 @@ public class OpenIDConnectService {
                         .setError("Could not find an identity provider with the identifier [" + idpHint + "].")
                         .createErrorPage();
             }
-
-            return Response.temporaryRedirect(
-                    Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), identityProviderModel, realm, accessCode)).build();
+            return redirectToIdentityProvider(idpHint, accessCode);
         }
 
         response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
@@ -890,16 +888,18 @@ public class OpenIDConnectService {
             return oauth.cancelLogin(clientSession);
         }
 
-        List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
+        List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
+        for (IdentityProviderModel identityProvider : identityProviders) {
+            if (identityProvider.isAuthenticateByDefault()) {
+                return redirectToIdentityProvider(identityProvider.getId(), accessCode);
+            }
+        }
 
+        List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
         if (requiredCredentials.isEmpty()) {
-            List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
-
             if (!identityProviders.isEmpty()) {
                 if (identityProviders.size() == 1) {
-                    return Response.temporaryRedirect(
-                            Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), identityProviders.get(0), this.realm, accessCode))
-                            .build();
+                    return redirectToIdentityProvider(identityProviders.get(0).getId(), accessCode);
                 }
 
                 return Flows.forms(session, realm, null, uriInfo).setError("Realm [" + this.realm.getName() + "] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.").createErrorPage();
@@ -1197,6 +1197,13 @@ public class OpenIDConnectService {
         return Response.status(status).entity(e).type("application/json").build();
     }
 
+    private Response redirectToIdentityProvider(String providerId, String accessCode) {
+        logger.debug("Automatically redirect to identity provider: " + providerId);
+        return Response.temporaryRedirect(
+                Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode))
+                .build();
+    }
+
     TokenManager getTokenManager() {
         return this.tokenManager;
     }
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 70b3ebb..241b37a 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,7 +46,6 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.TimeBasedOTP;
 import org.keycloak.protocol.oidc.OpenIDConnect;
@@ -684,14 +683,9 @@ public class AccountService {
                     clientSession.setRedirectUri(redirectUri);
                     clientSession.setNote(OpenIDConnect.STATE_PARAM, UUID.randomUUID().toString());
 
-                    URI url = UriBuilder.fromUri(this.uriInfo.getBaseUri())
-                            .path(AuthenticationBrokerResource.class)
-                            .path(AuthenticationBrokerResource.class, "performLogin")
-                            .queryParam("provider_id", providerId)
-                            .queryParam("code", clientSessionCode.getCode())
-                            .build(this.realm.getName());
-
-                    return Response.temporaryRedirect(url).build();
+                    return Response.temporaryRedirect(
+                            Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, realm.getName(), clientSessionCode.getCode()))
+                            .build();
                 } catch (Exception spe) {
                     setReferrerOnPage();
                     return account.setError(Messages.IDENTITY_PROVIDER_REDIRECT_ERROR).createResponse(AccountPages.FEDERATED_IDENTITY);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index 42c3497..a8ea3a4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -106,7 +106,12 @@ public class IdentityProvidersResource {
         String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
         String enabled = formDataMap.get("enabled").get(0).getBodyAsString();
         String updateProfileFirstLogin = formDataMap.get("updateProfileFirstLogin").get(0).getBodyAsString();
-        String storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
+        String storeToken = "false";
+
+        if (formDataMap.containsKey("storeToken")) {
+            storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
+        }
+
         InputPart file = formDataMap.get("file").get(0);
         InputStream inputStream = file.getBody(InputStream.class, null);
         IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index 5dc22d2..3d7b19c 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -22,12 +22,10 @@
 package org.keycloak.services.resources.flows;
 
 import org.keycloak.OAuth2Constants;
-import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.OpenIDConnect;
 import org.keycloak.protocol.oidc.OpenIDConnectService;
 import org.keycloak.services.resources.AccountService;
-import org.keycloak.services.resources.AuthenticationBrokerResource;
+import org.keycloak.services.resources.IdentityBrokerService;
 import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.resources.ThemeResource;
@@ -68,21 +66,31 @@ public class Urls {
         return accountBase(baseUri).path(AccountService.class, "processFederatedIdentityUpdate").build(realmName);
     }
 
-    public static URI identityProviderAuthnRequest(URI baseURI, IdentityProviderModel identityProvider, RealmModel realm, String accessCode) {
-        UriBuilder uriBuilder = UriBuilder.fromUri(baseURI)
-                .path(AuthenticationBrokerResource.class)
-                .path(AuthenticationBrokerResource.class, "performLogin")
-                .replaceQueryParam("provider_id", identityProvider.getId());
+    public static URI identityProviderAuthnResponse(URI baseUri, String providerId, String realmName) {
+        return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+                .path(IdentityBrokerService.class, "handleResponseGet")
+                .build(realmName, providerId);
+    }
+
+    public static URI identityProviderAuthnRequest(URI baseUri, String providerId, String realmName, String accessCode) {
+        UriBuilder uriBuilder = realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+                .path(IdentityBrokerService.class, "performLogin");
 
         if (accessCode != null) {
             uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
         }
 
-        return uriBuilder.build(realm.getName());
+        return uriBuilder.build(realmName, providerId);
+    }
+
+    public static URI identityProviderRetrieveToken(URI baseUri, String providerId, String realmName) {
+        return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+                .path(IdentityBrokerService.class, "retrieveToken")
+                .build(realmName, providerId);
     }
 
-    public static URI identityProviderAuthnRequest(URI baseURI, IdentityProviderModel identityProvider, RealmModel realm) {
-        return identityProviderAuthnRequest(baseURI, identityProvider, realm, null);
+    public static URI identityProviderAuthnRequest(URI baseURI, String providerId, String realmName) {
+        return identityProviderAuthnRequest(baseURI, providerId, realmName, null);
     }
 
     public static URI accountTotpPage(URI baseUri, String realmId) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
new file mode 100644
index 0000000..1bbb581
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -0,0 +1,605 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.AuthenticationManager.AuthResult;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.managers.EventsManager;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.social.SocialIdentityProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
+import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
+import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_APP;
+import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
+
+/**
+ * <p></p>
+ *
+ * @author Pedro Igor
+ */
+@Path("/broker")
+public class IdentityBrokerService {
+
+    private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
+
+    private final RealmModel realmModel;
+
+    @Context
+    private UriInfo uriInfo;
+
+    @Context
+    private KeycloakSession session;
+
+    @Context
+    private ClientConnection clientConnection;
+
+    @Context
+    private HttpRequest request;
+    private EventBuilder event;
+
+    public IdentityBrokerService(RealmModel realmModel) {
+        if (realmModel == null) {
+            throw new IllegalArgumentException("Realm can not be null.");
+        }
+
+        this.realmModel = realmModel;
+    }
+
+    public void init() {
+        this.event = new EventsManager(this.realmModel, this.session, this.clientConnection).createEventBuilder().event(EventType.IDENTITY_PROVIDER_LOGIN);
+    }
+
+    @GET
+    @Path("/{provider_id}/login")
+    public Response performLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code) {
+        this.event.detail(Details.IDENTITY_PROVIDER, providerId);
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Sending authentication request to identity provider [%s].", providerId);
+        }
+
+        try {
+            ClientSessionCode clientSessionCode = parseClientSessionCode(code, providerId);
+            IdentityProvider identityProvider = getIdentityProvider(providerId);
+            AuthenticationResponse authenticationResponse = identityProvider.handleRequest(createAuthenticationRequest(providerId, clientSessionCode));
+
+            Response response = authenticationResponse.getResponse();
+
+            if (response != null) {
+                this.event.success();
+                if (isDebugEnabled()) {
+                    LOGGER.debugf("Identity provider [%s] is going to send a request [%s].", identityProvider, response);
+                }
+                return response;
+            }
+        } catch (IdentityBrokerException e) {
+            return redirectToErrorPage("Could not send authentication request to identity provider [" + providerId + "].", e);
+        } catch (Exception e) {
+            return redirectToErrorPage("Unexpected error when handling authentication request to identity provider [" + providerId + "].", e);
+        }
+
+        return redirectToErrorPage("Could not proceed with authentication request to identity provider.");
+    }
+
+    @GET
+    @Path("{provider_id}")
+    public Response handleResponseGet(@PathParam("provider_id") String providerId) {
+        return handleResponse(providerId);
+    }
+
+    @POST
+    @Path("{provider_id}")
+    public Response handleResponsePost(@PathParam("provider_id") String providerId) {
+        return handleResponse(providerId);
+    }
+
+    @Path("{provider_id}/token")
+    @OPTIONS
+    public Response retrieveTokenPreflight() {
+        return Cors.add(this.request, Response.ok()).auth().preflight().build();
+    }
+
+    @GET
+    @Path("{provider_id}/token")
+    public Response retrieveToken(@PathParam("provider_id") String providerId) {
+        return getToken(providerId, false);
+    }
+
+    private Response getToken(String providerId, boolean forceRetrieval) {
+        this.event.event(EventType.IDENTITY_PROVIDER_RETRIEVE_TOKEN);
+
+        try {
+            AppAuthManager authManager = new AppAuthManager();
+            AuthResult authResult = authManager.authenticateBearerToken(this.session, this.realmModel, this.uriInfo, this.clientConnection, this.request.getHttpHeaders());
+
+            if (authResult != null) {
+                String audience = authResult.getToken().getAudience();
+                ClientModel clientModel = this.realmModel.findClient(audience);
+
+                if (clientModel == null) {
+                    return badRequest("Invalid client.");
+                }
+
+                if (!clientModel.hasIdentityProvider(providerId)) {
+                    return corsResponse(badRequest("Client [" + audience + "] not authorized."), clientModel);
+                }
+
+                if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
+                    return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo)
+                            .setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
+                            .setAccessRequest("Your information from " + providerId + " identity provider.")
+                            .setClient(clientModel)
+                            .setUriInfo(this.uriInfo)
+                            .setActionUri(this.uriInfo.getRequestUri())
+                            .createOAuthGrant(), clientModel);
+                }
+
+                IdentityProvider identityProvider = getIdentityProvider(providerId);
+                IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
+
+                if (identityProviderConfig.isStoreToken()) {
+                    FederatedIdentityModel identity = this.session.users().getFederatedIdentity(authResult.getUser(), providerId, this.realmModel);
+
+                    if (identity == null) {
+                        return corsResponse(badRequest("User [" + authResult.getUser().getId() + "] is not associated with identity provider [" + providerId + "]."), clientModel);
+                    }
+
+                    this.event.success();
+
+                    return corsResponse(identityProvider.retrieveToken(identity), clientModel);
+                }
+
+                return corsResponse(badRequest("Identity Provider [" + providerId + "] does not support this operation."), clientModel);
+            }
+
+            return badRequest("Invalid token.");
+        } catch (IdentityBrokerException e) {
+            return redirectToErrorPage("Could not obtain token fron identity provider [" + providerId + "].", e);
+        }  catch (Exception e) {
+            return redirectToErrorPage("Unexpected error when retrieving token from identity provider [" + providerId + "].", e);
+        }
+    }
+
+    @POST
+    @Path("{provider_id}/token")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public Response consentTokenRetrieval(@PathParam("provider_id") String providerId,
+                                          MultivaluedMap<String, String> formData) {
+        if (formData.containsKey("cancel")) {
+            return redirectToErrorPage("Permission not approved.");
+        }
+
+        return getToken(providerId, true);
+    }
+
+    private Response handleResponse(String providerId) {
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Handling authentication response from identity provider [%s].", providerId);
+        }
+        this.event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
+        this.event.detail(Details.IDENTITY_PROVIDER, providerId);
+        IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
+
+        try {
+            IdentityProvider identityProvider = getIdentityProvider(providerId);
+            String relayState = identityProvider.getRelayState(createAuthenticationRequest(providerId, null));
+
+            if (relayState == null) {
+                return redirectToErrorPage("No relay state in response from identity identity [" + providerId + ".");
+            }
+
+            if (isDebugEnabled()) {
+                LOGGER.debugf("Relay state is valid: [%s].", relayState);
+            }
+
+            ClientSessionCode clientSessionCode = parseClientSessionCode(relayState, providerId);
+            AuthenticationResponse authenticationResponse = identityProvider.handleResponse(createAuthenticationRequest(providerId, clientSessionCode));
+            Response response = authenticationResponse.getResponse();
+
+            if (response != null) {
+                if (isDebugEnabled()) {
+                    LOGGER.debugf("Identity provider [%s] is going to send a response [%s].", identityProvider, response);
+                }
+                return response;
+            }
+
+            FederatedIdentity identity = authenticationResponse.getUser();
+
+            if (isDebugEnabled()) {
+                LOGGER.debugf("Identity provider [%s] returned with identity [%s].", providerId, identity);
+            }
+
+            if (!identityProviderConfig.isStoreToken()) {
+                if (isDebugEnabled()) {
+                    LOGGER.debugf("Token will not be stored for identity provider [%s].", providerId);
+                }
+                identity.setToken(null);
+            }
+
+            identity.setIdentityProviderId(providerId);
+
+            return performLocalAuthentication(identity, clientSessionCode);
+        } catch (IdentityBrokerException e) {
+            rollback();
+            return redirectToErrorPage("Authentication failed. Could not authenticate with identity provider [" + providerId + "].", e);
+        } catch (Exception e) {
+            rollback();
+            return redirectToErrorPage("Unexpected error when handling response from identity provider [" + providerId + "].", e);
+        } finally {
+            if (this.session.getTransaction().isActive()) {
+                this.session.getTransaction().commit();
+            }
+        }
+    }
+
+    private Response performLocalAuthentication(FederatedIdentity updatedIdentity, ClientSessionCode clientCode) {
+        ClientSessionModel clientSession = clientCode.getClientSession();
+        IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(updatedIdentity.getIdentityProviderId());
+        String providerId = identityProviderConfig.getId();
+        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, updatedIdentity.getId(),
+                updatedIdentity.getUsername(), updatedIdentity.getToken());
+
+        this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
+                .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+                .detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername());
+
+        UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
+
+        // Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
+        if (clientSession.getUserSession() != null) {
+            return performAccountLinking(clientSession, providerId, federatedIdentityModel, federatedUser);
+        }
+
+        if (federatedUser == null) {
+            try {
+                federatedUser = createUser(updatedIdentity);
+
+                if (identityProviderConfig.isUpdateProfileFirstLogin()) {
+                    if (isDebugEnabled()) {
+                        LOGGER.debugf("Identity provider requires update profile action.", federatedUser);
+                    }
+                    federatedUser.addRequiredAction(UPDATE_PROFILE);
+                }
+            } catch (Exception e) {
+                return redirectToLoginPage(e.getMessage(), clientCode);
+            }
+        }
+
+        updateFederatedIdentity(updatedIdentity, federatedUser);
+
+        UserSessionModel userSession = this.session.sessions()
+                .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false);
+
+        this.event.user(federatedUser);
+        this.event.session(userSession);
+
+        TokenManager.attachClientSession(userSession, clientSession);
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Performing local authentication for user [%s].", federatedUser);
+        }
+
+        return AuthenticationManager.nextActionAfterAuthentication(this.session, userSession, clientSession, this.clientConnection, this.request,
+                this.uriInfo, event);
+    }
+
+    private Response performAccountLinking(ClientSessionModel clientSession, String providerId, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
+        this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING);
+
+        if (federatedUser != null) {
+            return redirectToErrorPage("The identity returned by the identity provider [" + providerId + "] is already linked to other user.");
+        }
+
+        UserModel authenticatedUser = clientSession.getUserSession().getUser();
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, providerId, authenticatedUser);
+        }
+
+        if (!authenticatedUser.isEnabled()) {
+            fireErrorEvent(Errors.USER_DISABLED);
+            return redirectToErrorPage("User is disabled.");
+        }
+
+        if (!authenticatedUser.hasRole(this.realmModel.getApplicationByName(ACCOUNT_MANAGEMENT_APP).getRole(MANAGE_ACCOUNT))) {
+            fireErrorEvent(Errors.NOT_ALLOWED);
+            return redirectToErrorPage("Insufficient permissions to link identities.");
+        }
+
+        this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel);
+
+        this.event.success();
+
+        return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
+    }
+
+    private void updateFederatedIdentity(FederatedIdentity updatedIdentity, UserModel federatedUser) {
+        FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, updatedIdentity.getIdentityProviderId(), this.realmModel);
+
+        federatedIdentityModel.setToken(updatedIdentity.getToken());
+
+        this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdentityProviderId());
+        }
+    }
+
+    private ClientSessionCode parseClientSessionCode(String code, String providerId) {
+        ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
+
+        if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
+            validateClientPermissions(clientCode, providerId);
+            ClientSessionModel clientSession = clientCode.getClientSession();
+
+            if (clientSession != null) {
+                ClientModel client = clientSession.getClient();
+
+                if (client != null) {
+                    LOGGER.debugf("Got authorization code from client [%s].", client.getClientId());
+                    this.event.client(client);
+                }
+
+                if (clientSession.getUserSession() != null) {
+                    this.event.session(clientSession.getUserSession());
+                }
+            }
+
+            if (isDebugEnabled()) {
+                LOGGER.debugf("Authorization code is valid.");
+            }
+
+            return clientCode;
+        }
+
+        throw new IdentityBrokerException("Invalid code, please login again through your application.");
+    }
+
+    private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode clientSessionCode) {
+        ClientSessionModel clientSession = null;
+        String relayState = null;
+
+        if (clientSessionCode != null) {
+            clientSession = clientSessionCode.getClientSession();
+            relayState = clientSessionCode.getCode();
+        }
+
+        return new AuthenticationRequest(this.session, this.realmModel, clientSession, this.request, this.uriInfo, relayState, getRedirectUri(providerId));
+    }
+
+    private String getRedirectUri(String providerId) {
+        return Urls.identityProviderAuthnResponse(this.uriInfo.getBaseUri(), providerId, this.realmModel.getName()).toString();
+    }
+
+    private Response redirectToErrorPage(String message) {
+        return redirectToErrorPage(message, null);
+    }
+
+    private Response redirectToErrorPage(String message, Throwable throwable) {
+        fireErrorEvent(message, throwable);
+        return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message);
+    }
+
+    private Response badRequest(String message) {
+        fireErrorEvent(message);
+        return Flows.errors().error(message, Status.BAD_REQUEST);
+    }
+
+    private Response redirectToLoginPage(String message, ClientSessionCode clientCode) {
+        fireErrorEvent(message);
+        return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo)
+                .setClientSessionCode(clientCode.getCode())
+                .setError(message)
+                .createLogin();
+    }
+
+    private IdentityProvider getIdentityProvider(String providerId) {
+        IdentityProviderModel identityProviderModel = this.realmModel.getIdentityProviderById(providerId);
+
+        if (identityProviderModel != null) {
+            IdentityProviderFactory providerFactory = getIdentityProviderFactory(identityProviderModel);
+
+            if (providerFactory == null) {
+                throw new IdentityBrokerException("Could not find factory for identity provider [" + providerId + "].");
+            }
+
+            return providerFactory.create(identityProviderModel);
+        }
+
+        throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
+    }
+
+    private IdentityProviderFactory getIdentityProviderFactory(IdentityProviderModel model) {
+        Map<String, IdentityProviderFactory> availableProviders = new HashMap<String, IdentityProviderFactory>();
+        List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
+
+        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
+        allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
+
+        for (ProviderFactory providerFactory : allProviders) {
+            availableProviders.put(providerFactory.getId(), (IdentityProviderFactory) providerFactory);
+        }
+
+        return availableProviders.get(model.getProviderId());
+    }
+
+    private IdentityProviderModel getIdentityProviderConfig(String providerId) {
+        for (IdentityProviderModel model : this.realmModel.getIdentityProviders()) {
+            if (model.getId().equals(providerId)) {
+                return model;
+            }
+        }
+
+        throw new IdentityBrokerException("Configuration for identity provider [" + providerId + "] not found.");
+    }
+
+    private void validateClientPermissions(ClientSessionCode clientSessionCode, String providerId) {
+        ClientSessionModel clientSession = clientSessionCode.getClientSession();
+        ClientModel clientModel = clientSession.getClient();
+
+        if (clientModel == null) {
+            throw new IdentityBrokerException("Invalid client.");
+        }
+
+        if (!clientModel.hasIdentityProvider(providerId)) {
+            throw new IdentityBrokerException("Client [" + clientModel.getClientId() + "] not authorized to authenticate with identity provider [" + providerId + "].");
+        }
+    }
+
+    private UserModel createUser(FederatedIdentity updatedIdentity) {
+        FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdentityProviderId(), updatedIdentity.getId(),
+                updatedIdentity.getUsername(), updatedIdentity.getToken());
+        // Check if no user already exists with this username or email
+        UserModel existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
+
+        if (existingUser != null) {
+            fireErrorEvent(Errors.FEDERATED_IDENTITY_EMAIL_EXISTS);
+            throw new IdentityBrokerException("federatedIdentityEmailExists");
+        }
+
+        existingUser = this.session.users().getUserByUsername(updatedIdentity.getUsername(), this.realmModel);
+
+        if (existingUser != null) {
+            fireErrorEvent(Errors.FEDERATED_IDENTITY_USERNAME_EXISTS);
+            throw new IdentityBrokerException("federatedIdentityUsernameExists");
+        }
+
+        // Check if realm registration is allowed
+        if (!this.realmModel.isRegistrationAllowed()) {
+            fireErrorEvent(Errors.FEDERATED_IDENTITY_DISABLED_REGISTRATION);
+            throw new IdentityBrokerException("federatedIdentityDisabledRegistration");
+        }
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Creating account from identity [%s].", federatedIdentityModel);
+        }
+
+        UserModel federatedUser = this.session.users().addUser(this.realmModel, updatedIdentity.getUsername());
+
+        if (isDebugEnabled()) {
+            LOGGER.debugf("Account [%s] created.", federatedUser);
+        }
+
+        federatedUser.setEnabled(true);
+        federatedUser.setFirstName(updatedIdentity.getFirstName());
+        federatedUser.setLastName(updatedIdentity.getLastName());
+        federatedUser.setEmail(updatedIdentity.getEmail());
+
+        this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
+
+        this.event.clone().user(federatedUser).event(EventType.REGISTER)
+                .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
+                .detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername())
+                .removeDetail("auth_method")
+                .success();
+
+        return federatedUser;
+    }
+
+    private Response corsResponse(Response response, ClientModel clientModel) {
+        return Cors.add(this.request, Response.fromResponse(response)).auth().allowedOrigins(clientModel).build();
+    }
+
+    private void fireErrorEvent(String message, Throwable throwable) {
+        if (!this.event.getEvent().getType().toString().endsWith("_ERROR")) {
+            boolean newTransaction = !this.session.getTransaction().isActive();
+
+            try {
+                if (newTransaction) {
+                    this.session.getTransaction().begin();
+                }
+
+                this.event.error(message);
+
+                if (newTransaction) {
+                    this.session.getTransaction().commit();
+                }
+            } catch (Exception e) {
+                LOGGER.error("Could not fire event.", e);
+                rollback();
+            }
+        }
+
+        if (throwable != null) {
+            LOGGER.error(message, throwable);
+        } else {
+            LOGGER.error(message);
+        }
+    }
+
+    private void fireErrorEvent(String message) {
+        fireErrorEvent(message, null);
+    }
+
+    private boolean isDebugEnabled() {
+        return LOGGER.isDebugEnabled();
+    }
+
+    private void rollback() {
+        if (this.session.getTransaction().isActive()) {
+            this.session.getTransaction().rollback();
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index f3044ea..11b59c8 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -72,7 +72,6 @@ public class KeycloakApplication extends Application {
 
         singletons.add(new ServerVersionResource());
         singletons.add(new RealmsResource());
-        singletons.add(new AuthenticationBrokerResource());
         singletons.add(new AdminRoot());
         classes.add(SkeletonKeyContextResolver.class);
         classes.add(QRCodeResource.class);
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index a7fe6c4..de6620c 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -1,14 +1,11 @@
 package org.keycloak.services.resources;
 
 import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.ClientConnection;
-import org.keycloak.Config;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
@@ -20,22 +17,18 @@ import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.managers.EventsManager;
 import org.keycloak.services.managers.RealmManager;
-import org.keycloak.util.StreamUtil;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
-import java.io.InputStream;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -190,7 +183,18 @@ public class RealmsResource {
         return realmResource;
     }
 
+    @Path("{realm}/broker")
+    public IdentityBrokerService getBrokerService(final @PathParam("realm") String name) {
+        RealmManager realmManager = new RealmManager(session);
+        RealmModel realm = locateRealm(name, realmManager);
 
+        IdentityBrokerService brokerService = new IdentityBrokerService(realm);
+        ResteasyProviderFactory.getInstance().injectProperties(brokerService);
+        
+        brokerService.init();
+
+        return brokerService;
+    }
 
 
 }
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
index 5266c68..8ebea50 100755
--- a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
@@ -5,6 +5,7 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
 /**
@@ -61,7 +62,7 @@ public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider imp
 
             return user;
         } catch (Exception e) {
-            throw new RuntimeException(e);
+            throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
         }
     }
 
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
index 7be16dc..df6a4d0 100755
--- a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -5,6 +5,7 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
 import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
 import org.keycloak.broker.oidc.util.SimpleHttp;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.social.SocialIdentityProvider;
 
 /**
@@ -37,7 +38,7 @@ public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider imple
 
             return user;
         } catch (Exception e) {
-            throw new RuntimeException(e);
+            throw new IdentityBrokerException("Could not obtain user profile from github.", e);
         }
     }
 
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
index d9ee567..c7d3492 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
@@ -26,6 +26,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
 import org.keycloak.broker.provider.AuthenticationRequest;
 import org.keycloak.broker.provider.AuthenticationResponse;
 import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
 import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.FederatedIdentityModel;
 import org.keycloak.social.SocialIdentityProvider;
@@ -68,7 +69,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
 
             return AuthenticationResponse.temporaryRedirect(authenticationUrl);
         } catch (Exception e) {
-            throw new RuntimeException(e);
+            throw new IdentityBrokerException("Could send authentication request to twitter.", e);
         }
     }
 
@@ -83,7 +84,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
         MultivaluedMap<String, String> queryParameters = request.getUriInfo().getQueryParameters();
 
         if (queryParameters.getFirst("denied") != null) {
-            throw new RuntimeException("Access denied.");
+            throw new IdentityBrokerException("Access denied.");
         }
 
         try {
@@ -121,7 +122,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
 
             return AuthenticationResponse.end(identity);
         } catch (Exception e) {
-            throw new RuntimeException(e);
+            throw new IdentityBrokerException("Could get user profile from twitter.", e);
         }
     }
 
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 1c912d3..edd0812 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -30,6 +30,7 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.IDToken;
+import org.keycloak.services.resources.flows.Urls;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
 import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
@@ -53,8 +54,10 @@ 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 javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import java.io.IOException;
+import java.net.URI;
 import java.util.List;
 import java.util.Set;
 
@@ -69,6 +72,8 @@ import static org.junit.Assert.assertTrue;
  */
 public abstract class AbstractIdentityProviderTest {
 
+    private static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
+
     @ClassRule
     public static BrokerKeyCloakRule brokerServerRule = new BrokerKeyCloakRule();
 
@@ -308,7 +313,7 @@ public abstract class AbstractIdentityProviderTest {
 
         UserSessionStatus userSessionStatus = retrieveSessionStatus();
         String accessToken = userSessionStatus.getAccessTokenString();
-        String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
+        URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
         final String authHeader = "Bearer " + accessToken;
         ClientRequestFilter authFilter = new ClientRequestFilter() {
             @Override
@@ -317,11 +322,10 @@ public abstract class AbstractIdentityProviderTest {
             }
         };
         Client client = ClientBuilder.newBuilder().register(authFilter).build();
-        UriBuilder authBase = UriBuilder.fromUri(tokenEndpointUrl);
-        WebTarget tokenEndpoint = client.target(authBase);
+        WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
         Response response = tokenEndpoint.request().get();
 
-        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
         assertNotNull(response.readEntity(String.class));
 
         driver.navigate().to("http://localhost:8081/test-app/logout");
@@ -364,14 +368,15 @@ public abstract class AbstractIdentityProviderTest {
         grantPage.accept();
 
         assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
+
         AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
-        String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
+        URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), getRealm().getName());
         String authHeader = "Bearer " + accessToken.getAccessToken();
         HtmlUnitDriver htmlUnitDriver = (WebRule.HtmlUnitDriver) this.driver;
 
         htmlUnitDriver.getWebClient().addRequestHeader(HttpHeaders.AUTHORIZATION, authHeader);
 
-        htmlUnitDriver.navigate().to(tokenEndpointUrl);
+        htmlUnitDriver.navigate().to(tokenEndpointUrl.toString());
 
         grantPage.assertCurrent();
         grantPage.accept();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
index 8ac0ee7..9b9d723 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
@@ -80,6 +80,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         identityProviderModel.setEnabled(false);
         identityProviderModel.setUpdateProfileFirstLogin(false);
         identityProviderModel.setStoreToken(true);
+        identityProviderModel.setAuthenticateByDefault(true);
 
         realm.updateIdentityProvider(identityProviderModel);
 
@@ -94,11 +95,13 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertFalse(identityProviderModel.isEnabled());
         assertFalse(identityProviderModel.isUpdateProfileFirstLogin());
         assertTrue(identityProviderModel.isStoreToken());
+        assertTrue(identityProviderModel.isAuthenticateByDefault());
 
         identityProviderModel.setName("Changed Name Again");
         identityProviderModel.getConfig().remove("config-added");
         identityProviderModel.setEnabled(true);
         identityProviderModel.setUpdateProfileFirstLogin(true);
+        identityProviderModel.setAuthenticateByDefault(false);
 
         realm.updateIdentityProvider(identityProviderModel);
 
@@ -109,8 +112,9 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
 
         assertEquals("Changed Name Again", identityProviderModel.getName());
         assertFalse(identityProviderModel.getConfig().containsKey("config-added"));
-        assertEquals(true, identityProviderModel.isEnabled());
-        assertEquals(true, identityProviderModel.isUpdateProfileFirstLogin());
+        assertTrue(identityProviderModel.isEnabled());
+        assertTrue(identityProviderModel.isUpdateProfileFirstLogin());
+        assertFalse(identityProviderModel.isAuthenticateByDefault());
     }
 
     @Test
@@ -175,6 +179,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("Google", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(true, config.isStoreToken());
         assertEquals("clientId", config.getClientId());
         assertEquals("clientSecret", config.getClientSecret());
         assertEquals(GoogleIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -192,6 +198,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("SAML Signed IdP", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(false, config.isStoreToken());
         assertEquals("http://localhost:8082/auth/realms/realm-with-saml-identity-provider/protocol/saml", config.getSingleSignOnServiceUrl());
         assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", config.getNameIDPolicyFormat());
         assertEquals("MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin", config.getSigningCertificate());
@@ -211,6 +219,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("OIDC IdP", config.getName());
         assertEquals(false, config.isEnabled());
         assertEquals(false, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(false, config.isStoreToken());
         assertEquals("clientId", config.getClientId());
         assertEquals("clientSecret", config.getClientSecret());
     }
@@ -224,6 +234,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("Facebook", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(false, config.isStoreToken());
         assertEquals("clientId", config.getClientId());
         assertEquals("clientSecret", config.getClientSecret());
         assertEquals(FacebookIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -240,6 +252,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("GitHub", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(false, config.isStoreToken());
         assertEquals("clientId", config.getClientId());
         assertEquals("clientSecret", config.getClientSecret());
         assertEquals(GitHubIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -256,6 +270,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("Twitter", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
+        assertEquals(true, config.isStoreToken());
         assertEquals("clientId", config.getClientId());
         assertEquals("clientSecret", config.getClientSecret());
     }
@@ -269,6 +285,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
         assertEquals("Kerberos", config.getName());
         assertEquals(true, config.isEnabled());
         assertEquals(true, config.isUpdateProfileFirstLogin());
+        assertEquals(false, config.isAuthenticateByDefault());
         assertEquals("HTTP/server.domain.org@DOMAIN.ORG", config.getServerPrincipal());
         assertEquals("/etc/http.keytab", config.getKeyTab());
         assertTrue(config.getDebug());
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 eb71e08..a1ef54c 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
@@ -286,7 +286,7 @@ public class LoginTest {
             loginPage.login("login@test.com", "password");
 
             loginPage.assertCurrent();
-            Assert.assertEquals("Login timeout or unknown action. Please login again", loginPage.getError());
+            Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
 
             events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().assertEvent();
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index d308c3e..580ba2a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -164,7 +164,7 @@ public class LoginTotpTest {
             loginTotpPage.login(totp.generate("totpSecret"));
 
             loginPage.assertCurrent();
-            Assert.assertEquals("Login timeout or unknown action. Please login again", loginPage.getError());
+            Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
 
             AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("expired_code")
                     .user((String)null)
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
index d7831a7..dbed987 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
@@ -12,7 +12,7 @@
         "enabled": true,
         "secret": "secret",
         "redirectUris": [
-          "http://localhost:8081/auth/broker/realm-with-broker/kc-oidc-idp"
+          "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp"
         ],
         "claims": {
           "name" : true,
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
index 4bf96ff..1972224 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
@@ -11,7 +11,7 @@
             "name": "http://localhost:8081/auth/",
             "enabled": true,
             "redirectUris": [
-                "http://localhost:8081/auth/broker/realm-with-broker/kc-saml-idp-basic"
+                "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic"
             ],
             "attributes": {
                 "saml.authnstatement": "true"
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
index 82db4ea..3d22c42 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
@@ -11,7 +11,7 @@
             "name": "http://localhost:8081/auth/",
             "enabled": true,
             "redirectUris": [
-                "http://localhost:8081/auth/broker/realm-with-broker/kc-saml-signed-idp"
+                "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp"
             ],
             "attributes": {
                 "saml.assertion.signature": "true",
diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
index 182b131..bc62fa8 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
@@ -15,6 +15,7 @@
             "name" : "Google",
             "enabled": true,
             "updateProfileFirstLogin" : "true",
+            "storeToken": "true",
             "config": {
                 "clientId": "clientId",
                 "clientSecret": "clientSecret"
@@ -40,6 +41,7 @@
             "name" : "GitHub",
             "enabled": true,
             "updateProfileFirstLogin" : "true",
+            "storeToken": "false",
             "config": {
                 "authorizationUrl": "authorizationUrl",
                 "tokenUrl": "tokenUrl",
@@ -54,6 +56,7 @@
             "name" : "Twitter",
             "enabled": true,
             "updateProfileFirstLogin" : "true",
+            "storeToken": true,
             "config": {
                 "authorizationUrl": "authorizationUrl",
                 "tokenUrl": "tokenUrl",
@@ -116,6 +119,7 @@
             "name" : "OIDC IdP",
             "enabled": false,
             "updateProfileFirstLogin" : "false",
+            "authenticateByDefault" : "false",
             "config": {
                 "clientId": "clientId",
                 "clientSecret": "clientSecret",
@@ -148,6 +152,7 @@
             "name" : "Kerberos",
             "enabled": true,
             "updateProfileFirstLogin" : "true",
+            "authenticateByDefault" : "false",
             "config": {
                 "serverPrincipal": "HTTP/server.domain.org@DOMAIN.ORG",
                 "keyTab": "/etc/http.keytab",