diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java index d5c5608032..3dc0ff2f8d 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCAdvancedConfigWrapper.java @@ -27,6 +27,8 @@ import org.keycloak.utils.StringUtil; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author Marek Posolda @@ -378,7 +380,7 @@ public class OIDCAdvancedConfigWrapper extends AbstractClientConfigWrapper { public List getPostLogoutRedirectUris() { List postLogoutRedirectUris = getAttributeMultivalued(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS); - if(postLogoutRedirectUris == null || postLogoutRedirectUris.isEmpty() || postLogoutRedirectUris.get(0).equals("+")) { + if(postLogoutRedirectUris == null || postLogoutRedirectUris.isEmpty()) { if(clientModel != null) { return new ArrayList(clientModel.getRedirectUris()); } @@ -390,6 +392,18 @@ public class OIDCAdvancedConfigWrapper extends AbstractClientConfigWrapper { else if(postLogoutRedirectUris.get(0).equals("-")) { return new ArrayList(); } + else if (postLogoutRedirectUris.contains("+")) { + Set returnedPostLogoutRedirectUris = postLogoutRedirectUris.stream() + .filter(uri -> !"+".equals(uri)).collect(Collectors.toSet()); + + if(clientModel != null) { + returnedPostLogoutRedirectUris.addAll(clientModel.getRedirectUris()); + } + else if(clientRep != null) { + returnedPostLogoutRedirectUris.addAll(clientRep.getRedirectUris()); + } + return new ArrayList<>(returnedPostLogoutRedirectUris); + } else { return postLogoutRedirectUris; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java index 914c5d4c79..b0da4a126b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RPInitiatedLogoutTest.java @@ -98,6 +98,8 @@ import org.openqa.selenium.NoSuchElementException; */ public class RPInitiatedLogoutTest extends AbstractTestRealmKeycloakTest { + public static final String DUMMY_POST_LOGOUT_URI = "http://127.0.0.1:4321/thisisatest"; + @Rule public AssertEvents events = new AssertEvents(this); @@ -720,6 +722,50 @@ public class RPInitiatedLogoutTest extends AbstractTestRealmKeycloakTest { assertCurrentUrlEquals(APP_REDIRECT_URI + "?state=something2"); } + @Test + public void logoutWithClientIdAndPostLogoutRedirectUriWhenUsingPostLogoutRedirectUriAndPlusFirst() throws IOException { + doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri( + String.join(Constants.CFG_DELIMITER, + "+", + DUMMY_POST_LOGOUT_URI), + DUMMY_POST_LOGOUT_URI); + } + + @Test + public void logoutWithClientIdAndPostLogoutRedirectUriWhenUsingPostLogoutRedirectUriAndPlusLast() throws IOException { + doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri( + String.join(Constants.CFG_DELIMITER, + DUMMY_POST_LOGOUT_URI, + "+"), + DUMMY_POST_LOGOUT_URI); + } + + @Test + public void logoutWithClientIdAndPostLogoutRedirectUriWhenUsingAppRedirectUriAndAdditionalPostLogoutUriAndPlusFirstAndLast() throws IOException { + doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri( + String.join(Constants.CFG_DELIMITER, + "+", + DUMMY_POST_LOGOUT_URI, + "+"), + APP_REDIRECT_URI); + } + + @Test + public void logoutWithClientIdAndPostLogoutRedirectUriWhenUsingAppRedirectUriAndAdditionalPostLogoutUriAndPlusLast() throws IOException { + doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri( + String.join(Constants.CFG_DELIMITER, + DUMMY_POST_LOGOUT_URI, + "+"), + APP_REDIRECT_URI); + } + + @Test + public void logoutWithClientIdAndPostLogoutRedirectUriWhenWhenUsingAppRedirectUriAndPlus() throws IOException { + doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri( + "+", + APP_REDIRECT_URI); + } + @Test public void logoutWithBadClientId() { @@ -1142,4 +1188,23 @@ public class RPInitiatedLogoutTest extends AbstractTestRealmKeycloakTest { return false; } } + + private void doLogoutTestWithPostLogoutRedirectAttributeAndSpecifiedPostLogoutRedirectUri(String postLogoutRedirectAttr, String postLogoutRedirectUri) throws IOException { + try (Closeable accountClientUpdater = ClientAttributeUpdater.forClient(adminClient, "test", "test-app" ) + .setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, postLogoutRedirectAttr).update()) { + + OAuthClient.AccessTokenResponse tokenResponse = loginUser(); + + String logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(postLogoutRedirectUri).clientId("test-app").build(); + driver.navigate().to(logoutUrl); + + // Assert logout confirmation page as id_token_hint was not sent. Session still exists. Assert default language on logout page (English) + logoutConfirmPage.assertCurrent(); + Assert.assertEquals("English", logoutConfirmPage.getLanguageDropdownText()); + MatcherAssert.assertThat(true, is(isSessionActive(tokenResponse.getSessionState()))); + events.assertEmpty(); + + // We don't need to go further as the intent is that other tests will cover redirection + } + } }