Details
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
index fbc2aa2..a9e189a 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -655,20 +655,46 @@ public class SamlService extends AuthorizationEndpointBase {
event.error(Errors.INVALID_CLIENT);
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, "Wrong client protocol.");
}
- if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) == null) {
- logger.error("SAML assertion consumer url not set up");
- event.error(Errors.INVALID_REDIRECT_URI);
- return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REDIRECT_URI);
- }
session.getContext().setClient(client);
AuthenticationSessionModel authSession = getOrCreateLoginSessionForIdpInitiatedSso(this.session, this.realm, client, relayState);
+ if (authSession == null) {
+ logger.error("SAML assertion consumer url not set up");
+ event.error(Errors.INVALID_REDIRECT_URI);
+ return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REDIRECT_URI);
+ }
return newBrowserAuthentication(authSession, false, false);
}
/**
+ * Checks the client configuration to return the redirect URL and the binding type.
+ * POST is preferred, only if the SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE
+ * and management URL are empty REDIRECT is chosen.
+ *
+ * @param client Client to create client session for
+ * @return a two string array [samlUrl, bindingType] or null if error
+ */
+ private String[] getUrlAndBindingForIdpInitiatedSso(ClientModel client) {
+ String postUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
+ String getUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE);
+ if (postUrl != null && !postUrl.trim().isEmpty()) {
+ // first the POST binding URL
+ return new String[] {postUrl.trim(), SamlProtocol.SAML_POST_BINDING};
+ } else if (client.getManagementUrl() != null && !client.getManagementUrl().trim().isEmpty()) {
+ // second the management URL and POST
+ return new String[] {client.getManagementUrl().trim(), SamlProtocol.SAML_POST_BINDING};
+ } else if (getUrl != null && !getUrl.trim().isEmpty()){
+ // last option REDIRECT binding and URL
+ return new String[] {getUrl.trim(), SamlProtocol.SAML_REDIRECT_BINDING};
+ } else {
+ // error
+ return null;
+ }
+ }
+
+ /**
* Creates a client session object for SAML IdP-initiated SSO session.
* The session takes the parameters from from client definition,
* namely binding type and redirect URL.
@@ -677,29 +703,21 @@ public class SamlService extends AuthorizationEndpointBase {
* @param realm Realm to create client session in
* @param client Client to create client session for
* @param relayState Optional relay state - free field as per SAML specification
- * @return
+ * @return The auth session model or null if there is no SAML url is found
*/
public AuthenticationSessionModel getOrCreateLoginSessionForIdpInitiatedSso(KeycloakSession session, RealmModel realm, ClientModel client, String relayState) {
- String bindingType = SamlProtocol.SAML_POST_BINDING;
- if (client.getManagementUrl() == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE) == null && client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE) != null) {
- bindingType = SamlProtocol.SAML_REDIRECT_BINDING;
- }
-
- String redirect;
- if (bindingType.equals(SamlProtocol.SAML_REDIRECT_BINDING)) {
- redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE);
- } else {
- redirect = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
- }
- if (redirect == null) {
- redirect = client.getManagementUrl();
+ String[] bindingProperties = getUrlAndBindingForIdpInitiatedSso(client);
+ if (bindingProperties == null) {
+ return null;
}
+ String redirect = bindingProperties[0];
+ String bindingType = bindingProperties[1];
AuthenticationSessionModel authSession = createAuthenticationSession(client, null);
authSession.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
authSession.setAction(AuthenticationSessionModel.Action.AUTHENTICATE.name());
- authSession.setClientNote(SamlProtocol.SAML_BINDING, SamlProtocol.SAML_POST_BINDING);
+ authSession.setClientNote(SamlProtocol.SAML_BINDING, bindingType);
authSession.setClientNote(SamlProtocol.SAML_IDP_INITIATED_LOGIN, "true");
authSession.setRedirectUri(redirect);
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 66278a7..0b2dc11 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -1052,6 +1052,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
SamlService samlService = (SamlService) factory.createProtocolEndpoint(realmModel, event);
ResteasyProviderFactory.getInstance().injectProperties(samlService);
AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, realmModel, oClient.get(), null);
+ if (authSession == null) {
+ event.error(Errors.INVALID_REDIRECT_URI);
+ return ParsedCodeContext.response(redirectToErrorPage(Response.Status.BAD_REQUEST, Messages.INVALID_REDIRECT_URI));
+ }
return ParsedCodeContext.clientSessionCode(new ClientSessionCode<>(session, this.realmModel, authSession));
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java
index 0445b5e..81d5e9f 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/updaters/ClientAttributeUpdater.java
@@ -102,4 +102,8 @@ public class ClientAttributeUpdater extends ServerResourceUpdater<ClientAttribut
return new RoleScopeUpdater(resource.getScopeMappings().clientLevel(clientUUID));
}
+ public ClientAttributeUpdater setAdminUrl(String adminUrl) {
+ rep.setAdminUrl(adminUrl);
+ return this;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/IdpInitiatedLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/IdpInitiatedLoginTest.java
index 0f1dd16..564596d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/IdpInitiatedLoginTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/IdpInitiatedLoginTest.java
@@ -16,11 +16,11 @@
*/
package org.keycloak.testsuite.saml;
-import org.apache.http.client.HttpResponseException;
+import java.io.Closeable;
+import java.io.IOException;
import org.junit.Assert;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.UsersResource;
-import org.keycloak.client.registration.HttpErrorException;
import org.keycloak.dom.saml.v2.protocol.ResponseType;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.saml.SamlProtocol;
@@ -45,6 +45,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
+import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import static org.keycloak.testsuite.util.Matchers.bodyHC;
import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
@@ -55,7 +56,7 @@ import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
public class IdpInitiatedLoginTest extends AbstractSamlTest {
@Test
- public void testIdpInitiatedLogin() {
+ public void testIdpInitiatedLoginPost() {
new SamlClientBuilder()
.idpInitiatedLogin(getAuthServerSamlEndpoint(REALM_NAME), "sales-post").build()
.login().user(bburkeUser).build()
@@ -72,6 +73,53 @@ public class IdpInitiatedLoginTest extends AbstractSamlTest {
}
@Test
+ public void testIdpInitiatedLoginPostAdminUrl() throws IOException {
+ String url = adminClient.realm(REALM_NAME).clients().findByClientId(SAML_CLIENT_ID_SALES_POST).get(0)
+ .getAttributes().get(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
+ try (Closeable c = ClientAttributeUpdater.forClient(adminClient, REALM_NAME, SAML_CLIENT_ID_SALES_POST)
+ .setAdminUrl(url)
+ .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, null)
+ .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, null)
+ .update()) {
+ new SamlClientBuilder()
+ .idpInitiatedLogin(getAuthServerSamlEndpoint(REALM_NAME), "sales-post").build()
+ .login().user(bburkeUser).build()
+ .processSamlResponse(Binding.POST)
+ .transformObject(ob -> {
+ assertThat(ob, Matchers.isSamlResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+ ResponseType resp = (ResponseType) ob;
+ assertThat(resp.getDestination(), is(SAML_ASSERTION_CONSUMER_URL_SALES_POST));
+ return null;
+ })
+ .build()
+ .execute();
+ }
+ }
+
+ @Test
+ public void testIdpInitiatedLoginRedirect() throws IOException {
+ String url = adminClient.realm(REALM_NAME).clients().findByClientId(SAML_CLIENT_ID_SALES_POST).get(0)
+ .getAttributes().get(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
+ try (Closeable c = ClientAttributeUpdater.forClient(adminClient, REALM_NAME, SAML_CLIENT_ID_SALES_POST)
+ .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE, null)
+ .setAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE, url)
+ .update()) {
+ new SamlClientBuilder()
+ .idpInitiatedLogin(getAuthServerSamlEndpoint(REALM_NAME), "sales-post").build()
+ .login().user(bburkeUser).build()
+ .processSamlResponse(Binding.REDIRECT)
+ .transformObject(ob -> {
+ assertThat(ob, Matchers.isSamlResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+ ResponseType resp = (ResponseType) ob;
+ assertThat(resp.getDestination(), is(SAML_ASSERTION_CONSUMER_URL_SALES_POST));
+ return null;
+ })
+ .build()
+ .execute();
+ }
+ }
+
+ @Test
public void testTwoConsequentIdpInitiatedLogins() {
new SamlClientBuilder()
.idpInitiatedLogin(getAuthServerSamlEndpoint(REALM_NAME), "sales-post").build()