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
+ }
+ }
}