KEYCLOAK-9068: IDP-initiated-flow is not working with REDIRECT binding

This commit is contained in:
rmartinc 2018-12-12 09:26:46 +01:00 committed by Bruno Oliveira da Silva
parent 5011e07270
commit 3c44e6c377
4 changed files with 97 additions and 23 deletions

View file

@ -655,19 +655,45 @@ 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) {
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);
}
session.getContext().setClient(client);
AuthenticationSessionModel authSession = getOrCreateLoginSessionForIdpInitiatedSso(this.session, this.realm, client, relayState);
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,
@ -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);

View file

@ -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));
}

View file

@ -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;
}
}

View file

@ -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()
@ -71,6 +72,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()