keycloak-aplcache

Details

diff --git a/core/src/main/java/org/keycloak/constants/AdapterConstants.java b/core/src/main/java/org/keycloak/constants/AdapterConstants.java
index 976d6cb..18f21a8 100755
--- a/core/src/main/java/org/keycloak/constants/AdapterConstants.java
+++ b/core/src/main/java/org/keycloak/constants/AdapterConstants.java
@@ -29,4 +29,7 @@ public interface AdapterConstants {
 
     // Cookie used on adapter side to store token info. Used only when tokenStore is 'COOKIE'
     public static final String KEYCLOAK_ADAPTER_STATE_COOKIE = "KEYCLOAK_ADAPTER_STATE";
+
+    // Request parameter used to specify the identifier of the identity provider that should be used to authenticate an user
+    String K_IDP_HINT = "k_idp_hint";
 }
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index f0f4797..c49fa2c 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -1,6 +1,5 @@
 package org.keycloak.adapters;
 
-import org.apache.http.client.utils.URIBuilder;
 import org.jboss.logging.Logger;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.RSATokenVerifier;
@@ -17,6 +16,8 @@ import java.io.IOException;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicLong;
 
+import static org.keycloak.constants.AdapterConstants.K_IDP_HINT;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -131,6 +132,9 @@ public class OAuthRequestAuthenticator {
         String loginHint = getQueryParamValue("login_hint");
         url = UriUtils.stripQueryParam(url,"login_hint");
 
+        String idpHint = getQueryParamValue(K_IDP_HINT);
+        url = UriUtils.stripQueryParam(url, K_IDP_HINT);
+
         KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
                 .queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
                 .queryParam(OAuth2Constants.REDIRECT_URI, url)
@@ -139,6 +143,9 @@ public class OAuthRequestAuthenticator {
         if(loginHint != null && loginHint.length() > 0){
             redirectUriBuilder.queryParam("login_hint",loginHint);
         }
+        if (idpHint != null && idpHint.length() > 0) {
+            redirectUriBuilder.queryParam(K_IDP_HINT,idpHint);
+        }
 
         return redirectUriBuilder.build().toString();
     }
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 99e1859..d6f79dd 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
@@ -76,6 +76,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.keycloak.constants.AdapterConstants.K_IDP_HINT;
+
 /**
  * Resource class for the oauth/openid connect token service
  *
@@ -851,7 +853,8 @@ public class OpenIDConnectService {
                               @QueryParam(OpenIDConnect.SCOPE_PARAM) String scopeParam,
                               @QueryParam(OpenIDConnect.STATE_PARAM) String state,
                               @QueryParam(OpenIDConnect.PROMPT_PARAM) String prompt,
-                              @QueryParam(OpenIDConnect.LOGIN_HINT_PARAM) String loginHint) {
+                              @QueryParam(OpenIDConnect.LOGIN_HINT_PARAM) String loginHint,
+                              @QueryParam(K_IDP_HINT) String idpHint) {
         event.event(EventType.LOGIN);
         FrontPageInitializer pageInitializer = new FrontPageInitializer();
         pageInitializer.responseType = responseType;
@@ -875,6 +878,20 @@ public class OpenIDConnectService {
         }
 
         String accessCode = new ClientSessionCode(realm, clientSession).getCode();
+
+        if (idpHint != null && !"".equals(idpHint)) {
+            IdentityProviderModel identityProviderModel = realm.getIdentityProviderById(idpHint);
+
+            if (identityProviderModel == null) {
+                return Flows.forms(session, realm, null, uriInfo)
+                        .setError("Could not find an identity provider with the identifier [" + idpHint + "].")
+                        .createErrorPage();
+            }
+
+            return Response.temporaryRedirect(
+                    Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), identityProviderModel, realm, accessCode)).build();
+        }
+
         List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
 
         if (requiredCredentials.isEmpty()) {
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 0367bb7..da5d014 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
@@ -29,12 +29,9 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.representations.IDToken;
-import org.keycloak.services.managers.RealmManager;
-import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
 import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
-import org.keycloak.testsuite.rule.AbstractKeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.openqa.selenium.By;
@@ -43,7 +40,6 @@ import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 
 import java.io.IOException;
-import java.net.URL;
 import java.util.List;
 import java.util.Set;
 
@@ -58,20 +54,7 @@ import static org.junit.Assert.assertTrue;
 public abstract class AbstractIdentityProviderTest {
 
     @ClassRule
-    public static AbstractKeycloakRule brokerServerRule = new AbstractKeycloakRule() {
-
-        @Override
-        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
-            server.importRealm(getClass().getResourceAsStream("/broker-test/test-realm-with-broker.json"));
-            URL url = getClass().getResource("/broker-test/test-app-keycloak.json");
-            deployApplication("test-app", "/test-app", UserSessionStatusServlet.class, url.getPath(), "manager");
-        }
-
-        @Override
-        protected String[] getTestRealms() {
-            return new String[] {"realm-with-broker"};
-        }
-    };
+    public static BrokerKeyCloakRule brokerServerRule = new BrokerKeyCloakRule();
 
     @Rule
     public WebRule webRule = new WebRule(this);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/BrokerKeyCloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/BrokerKeyCloakRule.java
new file mode 100644
index 0000000..be9503d
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/BrokerKeyCloakRule.java
@@ -0,0 +1,45 @@
+/*
+ * 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.testsuite.broker;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+import java.net.URL;
+
+/**
+ * @author pedroigor
+ */
+public class BrokerKeyCloakRule extends AbstractKeycloakRule {
+
+    @Override
+    protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+        server.importRealm(getClass().getResourceAsStream("/broker-test/test-realm-with-broker.json"));
+        URL url = getClass().getResource("/broker-test/test-app-keycloak.json");
+        deployApplication("test-app", "/test-app", UserSessionStatusServlet.class, url.getPath(), "manager");
+    }
+
+    @Override
+    protected String[] getTestRealms() {
+        return new String[] {"realm-with-broker"};
+    }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java
new file mode 100755
index 0000000..b46617e
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java
@@ -0,0 +1,81 @@
+package org.keycloak.testsuite.broker;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.OAuthGrantPage;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.testutils.KeycloakServer;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author pedroigor
+ */
+public class IdentityProviderHintTest {
+
+    @ClassRule
+    public static BrokerKeyCloakRule keycloakRule = new BrokerKeyCloakRule();
+
+    @ClassRule
+    public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+        @Override
+        protected void configureServer(KeycloakServer server) {
+            server.getConfig().setPort(8082);
+        }
+
+        @Override
+        protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+            server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
+        }
+    };
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    private WebDriver driver;
+
+    @WebResource
+    private LoginPage loginPage;
+
+    @WebResource
+    private OAuthGrantPage grantPage;
+
+    @Test
+    public void testSuccessfulRedirect() {
+        this.driver.navigate().to("http://localhost:8081/test-app?k_idp_hint=kc-oidc-idp");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+
+        // log in to identity provider
+        this.loginPage.login("test-user", "password");
+
+        // grant access to broker-app
+        this.grantPage.assertCurrent();
+        this.grantPage.accept();
+
+        // authenticated and redirected to app
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+        assertTrue(this.driver.getPageSource().contains("idToken"));
+    }
+
+    @Test
+    public void testInvalidIdentityProviderHint() {
+        this.driver.navigate().to("http://localhost:8081/test-app?k_idp_hint=invalid-idp-id");
+
+        assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
+
+        assertEquals("Could not find an identity provider with the identifier [invalid-idp-id].", this.driver.findElement(By.className("instruction")).getText());
+    }
+}