Support for post_logout_redirect_uris in OIDC client registration (#12282)
Closes #10135
This commit is contained in:
parent
ee0c67c0c8
commit
c00514d659
24 changed files with 263 additions and 2 deletions
|
@ -37,5 +37,9 @@
|
||||||
<artifactId>selenium-java</artifactId>
|
<artifactId>selenium-java</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.test.builders;
|
package org.keycloak.test.builders;
|
||||||
|
|
||||||
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -88,6 +89,9 @@ public class ClientBuilder {
|
||||||
|
|
||||||
if (rep.getRedirectUris() == null && rep.getRootUrl() != null)
|
if (rep.getRedirectUris() == null && rep.getRootUrl() != null)
|
||||||
rep.setRedirectUris(Collections.singletonList(rep.getRootUrl().concat("/*")));
|
rep.setRedirectUris(Collections.singletonList(rep.getRootUrl().concat("/*")));
|
||||||
|
if (OIDCAdvancedConfigWrapper.fromClientRepresentation(rep).getPostLogoutRedirectUris() == null) {
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(rep).setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
|
}
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* 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.connections.jpa.updater.liquibase.custom;
|
||||||
|
|
||||||
|
import liquibase.exception.CustomChangeException;
|
||||||
|
import liquibase.statement.core.InsertStatement;
|
||||||
|
import liquibase.structure.core.Table;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
|
||||||
|
public class JpaUpdate19_0_0_DefaultPostLogoutRedirectUri extends CustomKeycloakTask {
|
||||||
|
|
||||||
|
private static final String POST_LOGOUT_REDIRECT_URIS = "post.logout.redirect.uris";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generateStatementsImpl() throws CustomChangeException {
|
||||||
|
String sql = "SELECT DISTINCT CLIENT_ID FROM " + getTableName("REDIRECT_URIS");
|
||||||
|
|
||||||
|
try (PreparedStatement statement = jdbcConnection.prepareStatement(sql); ResultSet rs = statement.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
statements.add(
|
||||||
|
new InsertStatement(null, null, database.correctObjectName("CLIENT_ATTRIBUTES", Table.class))
|
||||||
|
.addColumnValue("CLIENT_ID", rs.getString(1))
|
||||||
|
.addColumnValue("NAME", POST_LOGOUT_REDIRECT_URIS)
|
||||||
|
.addColumnValue("VALUE", "+")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CustomChangeException(getTaskId() + ": Exception when extracting data from previous version", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTaskId() {
|
||||||
|
return "Default post_logout_redirect_uris (19.0.0)";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!--
|
||||||
|
~ * Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
|
~ * and other contributors as indicated by the @author tags.
|
||||||
|
~ *
|
||||||
|
~ * 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.
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
|
||||||
|
<changeSet author="keycloak" id="19.0.0-10135">
|
||||||
|
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate19_0_0_DefaultPostLogoutRedirectUri"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
|
@ -73,5 +73,6 @@
|
||||||
<include file="META-INF/jpa-changelog-15.0.0.xml"/>
|
<include file="META-INF/jpa-changelog-15.0.0.xml"/>
|
||||||
<include file="META-INF/jpa-changelog-17.0.0.xml"/>
|
<include file="META-INF/jpa-changelog-17.0.0.xml"/>
|
||||||
<include file="META-INF/jpa-changelog-18.0.0.xml"/>
|
<include file="META-INF/jpa-changelog-18.0.0.xml"/>
|
||||||
|
<include file="META-INF/jpa-changelog-19.0.0.xml"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -55,6 +55,7 @@ import org.keycloak.models.utils.DefaultKeyProviders;
|
||||||
import org.keycloak.models.utils.DefaultRequiredActions;
|
import org.keycloak.models.utils.DefaultRequiredActions;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
|
@ -464,6 +465,10 @@ public class LegacyExportImportManager implements ExportImportManager {
|
||||||
Map<String, ClientModel> appMap = new HashMap<String, ClientModel>();
|
Map<String, ClientModel> appMap = new HashMap<String, ClientModel>();
|
||||||
for (ClientRepresentation resourceRep : rep.getClients()) {
|
for (ClientRepresentation resourceRep : rep.getClients()) {
|
||||||
ClientModel app = RepresentationToModel.createClient(session, realm, resourceRep, mappedFlows);
|
ClientModel app = RepresentationToModel.createClient(session, realm, resourceRep, mappedFlows);
|
||||||
|
String postLogoutRedirectUris = app.getAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS);
|
||||||
|
if (postLogoutRedirectUris == null) {
|
||||||
|
app.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
}
|
||||||
appMap.put(app.getClientId(), app);
|
appMap.put(app.getClientId(), app);
|
||||||
|
|
||||||
ValidationUtil.validateClient(session, app, false, r -> {
|
ValidationUtil.validateClient(session, app, false, r -> {
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.keycloak.models.utils.DefaultKeyProviders;
|
||||||
import org.keycloak.models.utils.DefaultRequiredActions;
|
import org.keycloak.models.utils.DefaultRequiredActions;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.representations.idm.ApplicationRepresentation;
|
import org.keycloak.representations.idm.ApplicationRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
|
@ -449,6 +450,10 @@ public class MapExportImportManager implements ExportImportManager {
|
||||||
Map<String, ClientModel> appMap = new HashMap<>();
|
Map<String, ClientModel> appMap = new HashMap<>();
|
||||||
for (ClientRepresentation resourceRep : rep.getClients()) {
|
for (ClientRepresentation resourceRep : rep.getClients()) {
|
||||||
ClientModel app = RepresentationToModel.createClient(session, realm, resourceRep, mappedFlows);
|
ClientModel app = RepresentationToModel.createClient(session, realm, resourceRep, mappedFlows);
|
||||||
|
String postLogoutRedirectUris = app.getAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS);
|
||||||
|
if (postLogoutRedirectUris == null) {
|
||||||
|
app.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
}
|
||||||
appMap.put(app.getClientId(), app);
|
appMap.put(app.getClientId(), app);
|
||||||
|
|
||||||
ValidationUtil.validateClient(session, app, false, r -> {
|
ValidationUtil.validateClient(session, app, false, r -> {
|
||||||
|
|
|
@ -82,6 +82,8 @@ public final class OIDCConfigAttributes {
|
||||||
public static final String FRONT_CHANNEL_LOGOUT_URI = "frontchannel.logout.url";
|
public static final String FRONT_CHANNEL_LOGOUT_URI = "frontchannel.logout.url";
|
||||||
public static final String FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED = "frontchannel.logout.session.required";
|
public static final String FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED = "frontchannel.logout.session.required";
|
||||||
|
|
||||||
|
public static final String POST_LOGOUT_REDIRECT_URIS = "post.logout.redirect.uris";
|
||||||
|
|
||||||
private OIDCConfigAttributes() {
|
private OIDCConfigAttributes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.PartialImportRepresentation;
|
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
@ -118,6 +119,9 @@ public class ClientsPartialImport extends AbstractPartialImport<ClientRepresenta
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientModel client = RepresentationToModel.createClient(session, realm, clientRep);
|
ClientModel client = RepresentationToModel.createClient(session, realm, clientRep);
|
||||||
|
if(OIDCAdvancedConfigWrapper.fromClientModel(client).getPostLogoutRedirectUris() == null) {
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientModel(client).setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
|
}
|
||||||
RepresentationToModel.importAuthorizationSettings(clientRep, client, session);
|
RepresentationToModel.importAuthorizationSettings(clientRep, client, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,4 +356,27 @@ public class OIDCAdvancedConfigWrapper extends AbstractClientConfigWrapper {
|
||||||
setAttribute(ClientModel.TOS_URI, tosUri);
|
setAttribute(ClientModel.TOS_URI, tosUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getPostLogoutRedirectUris() {
|
||||||
|
List<String> postLogoutRedirectUris = getAttributeMultivalued(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS);
|
||||||
|
if(postLogoutRedirectUris == null || postLogoutRedirectUris.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (postLogoutRedirectUris.get(0).equals("+")) {
|
||||||
|
if(clientModel != null) {
|
||||||
|
return new ArrayList(clientModel.getRedirectUris());
|
||||||
|
}
|
||||||
|
else if(clientRep != null) {
|
||||||
|
return clientRep.getRedirectUris();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return postLogoutRedirectUris;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostLogoutRedirectUris(List<String> postLogoutRedirectUris) {
|
||||||
|
setAttributeMultivalued(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, postLogoutRedirectUris);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.SystemClientUtil;
|
import org.keycloak.models.utils.SystemClientUtil;
|
||||||
import org.keycloak.protocol.oidc.BackchannelLogoutResponse;
|
import org.keycloak.protocol.oidc.BackchannelLogoutResponse;
|
||||||
import org.keycloak.protocol.oidc.LogoutTokenValidationCode;
|
import org.keycloak.protocol.oidc.LogoutTokenValidationCode;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||||
import org.keycloak.protocol.oidc.OIDCProviderConfig;
|
import org.keycloak.protocol.oidc.OIDCProviderConfig;
|
||||||
|
@ -92,6 +93,15 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
||||||
|
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
||||||
|
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -233,7 +243,9 @@ public class LogoutEndpoint {
|
||||||
String validatedRedirectUri = null;
|
String validatedRedirectUri = null;
|
||||||
if (redirectUri != null) {
|
if (redirectUri != null) {
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
validatedRedirectUri = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
|
OIDCAdvancedConfigWrapper wrapper = OIDCAdvancedConfigWrapper.fromClientModel(client);
|
||||||
|
Set<String> postLogoutRedirectUris = wrapper.getPostLogoutRedirectUris() != null ? new HashSet(wrapper.getPostLogoutRedirectUris()) : new HashSet<>();
|
||||||
|
validatedRedirectUri = RedirectUtils.verifyRedirectUri(session, client.getRootUrl(), redirectUri, postLogoutRedirectUris, true);
|
||||||
} else if (clientId == null) {
|
} else if (clientId == null) {
|
||||||
/*
|
/*
|
||||||
* Only call verifyRealmRedirectUri, in case both clientId and client are null - otherwise
|
* Only call verifyRealmRedirectUri, in case both clientId and client are null - otherwise
|
||||||
|
|
|
@ -208,6 +208,10 @@ public class DescriptionConverter {
|
||||||
configWrapper.setTosUri(clientOIDC.getTosUri());
|
configWrapper.setTosUri(clientOIDC.getTosUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clientOIDC.getPostLogoutRedirectUris() != null) {
|
||||||
|
configWrapper.setPostLogoutRedirectUris(clientOIDC.getPostLogoutRedirectUris());
|
||||||
|
}
|
||||||
|
|
||||||
// CIBA
|
// CIBA
|
||||||
String backchannelTokenDeliveryMode = clientOIDC.getBackchannelTokenDeliveryMode();
|
String backchannelTokenDeliveryMode = clientOIDC.getBackchannelTokenDeliveryMode();
|
||||||
if (backchannelTokenDeliveryMode != null) {
|
if (backchannelTokenDeliveryMode != null) {
|
||||||
|
@ -403,6 +407,9 @@ public class DescriptionConverter {
|
||||||
if (config.getTokenEndpointAuthSigningAlg() != null) {
|
if (config.getTokenEndpointAuthSigningAlg() != null) {
|
||||||
response.setTokenEndpointAuthSigningAlg(config.getTokenEndpointAuthSigningAlg());
|
response.setTokenEndpointAuthSigningAlg(config.getTokenEndpointAuthSigningAlg());
|
||||||
}
|
}
|
||||||
|
if (config.getPostLogoutRedirectUris() != null) {
|
||||||
|
response.setPostLogoutRedirectUris(config.getPostLogoutRedirectUris());
|
||||||
|
}
|
||||||
response.setBackchannelLogoutUri(config.getBackchannelLogoutUrl());
|
response.setBackchannelLogoutUri(config.getBackchannelLogoutUrl());
|
||||||
response.setBackchannelLogoutSessionRequired(config.isBackchannelLogoutSessionRequired());
|
response.setBackchannelLogoutSessionRequired(config.isBackchannelLogoutSessionRequired());
|
||||||
response.setBackchannelLogoutSessionRequired(config.getBackchannelLogoutRevokeOfflineTokens());
|
response.setBackchannelLogoutSessionRequired(config.getBackchannelLogoutRevokeOfflineTokens());
|
||||||
|
|
|
@ -173,6 +173,7 @@ public class RealmManager {
|
||||||
String baseUrl = "/admin/" + realm.getName() + "/console/";
|
String baseUrl = "/admin/" + realm.getName() + "/console/";
|
||||||
adminConsole.setBaseUrl(baseUrl);
|
adminConsole.setBaseUrl(baseUrl);
|
||||||
adminConsole.addRedirectUri(baseUrl + "*");
|
adminConsole.addRedirectUri(baseUrl + "*");
|
||||||
|
adminConsole.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
adminConsole.setWebOrigins(Collections.singleton("+"));
|
adminConsole.setWebOrigins(Collections.singleton("+"));
|
||||||
|
|
||||||
adminConsole.setEnabled(true);
|
adminConsole.setEnabled(true);
|
||||||
|
@ -417,6 +418,7 @@ public class RealmManager {
|
||||||
String baseUrl = "/realms/" + realm.getName() + "/account/";
|
String baseUrl = "/realms/" + realm.getName() + "/account/";
|
||||||
accountClient.setBaseUrl(baseUrl);
|
accountClient.setBaseUrl(baseUrl);
|
||||||
accountClient.addRedirectUri(baseUrl + "*");
|
accountClient.addRedirectUri(baseUrl + "*");
|
||||||
|
accountClient.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
|
||||||
accountClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
accountClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
|
||||||
|
@ -451,6 +453,7 @@ public class RealmManager {
|
||||||
accountConsoleClient.setRootUrl(Constants.AUTH_BASE_URL_PROP);
|
accountConsoleClient.setRootUrl(Constants.AUTH_BASE_URL_PROP);
|
||||||
accountConsoleClient.setBaseUrl(baseUrl);
|
accountConsoleClient.setBaseUrl(baseUrl);
|
||||||
accountConsoleClient.addRedirectUri(baseUrl + "*");
|
accountConsoleClient.addRedirectUri(baseUrl + "*");
|
||||||
|
accountConsoleClient.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
|
|
||||||
accountConsoleClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
accountConsoleClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.models.UserSessionSpi;
|
import org.keycloak.models.UserSessionSpi;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
|
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.common.util.Retry;
|
import org.keycloak.common.util.Retry;
|
||||||
|
@ -98,6 +99,7 @@ public class ConcurrentLoginTest extends AbstractConcurrencyTest {
|
||||||
.directAccessGrants()
|
.directAccessGrants()
|
||||||
.redirectUris("*")
|
.redirectUris("*")
|
||||||
.addWebOrigin("*")
|
.addWebOrigin("*")
|
||||||
|
.attribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+")
|
||||||
.secret("password")
|
.secret("password")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.IdentityProviderSyncMode;
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||||
|
@ -72,6 +73,8 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
||||||
client.setAdminUrl(getConsumerRoot() +
|
client.setAdminUrl(getConsumerRoot() +
|
||||||
"/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint");
|
"/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_OIDC_ALIAS + "/endpoint");
|
||||||
|
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(client).setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
|
|
||||||
ProtocolMapperRepresentation emailMapper = new ProtocolMapperRepresentation();
|
ProtocolMapperRepresentation emailMapper = new ProtocolMapperRepresentation();
|
||||||
emailMapper.setName("email");
|
emailMapper.setName("email");
|
||||||
emailMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
emailMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
@ -170,6 +173,8 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
|
||||||
client.setBaseUrl(getConsumerRoot() +
|
client.setBaseUrl(getConsumerRoot() +
|
||||||
"/auth/realms/" + REALM_CONS_NAME + "/app");
|
"/auth/realms/" + REALM_CONS_NAME + "/app");
|
||||||
|
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(client).setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
|
|
||||||
return Collections.singletonList(client);
|
return Collections.singletonList(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -633,6 +633,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
clientRep.setPublicClient(Boolean.FALSE);
|
clientRep.setPublicClient(Boolean.FALSE);
|
||||||
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
|
clientRep.setServiceAccountsEnabled(Boolean.TRUE);
|
||||||
clientRep.setRedirectUris(Collections.singletonList(ServerURLs.getAuthServerContextRoot() + "/auth/realms/master/app/auth"));
|
clientRep.setRedirectUris(Collections.singletonList(ServerURLs.getAuthServerContextRoot() + "/auth/realms/master/app/auth"));
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
op.accept(clientRep);
|
op.accept(clientRep);
|
||||||
Response resp = adminClient.realm(REALM_NAME).clients().create(clientRep);
|
Response resp = adminClient.realm(REALM_NAME).clients().create(clientRep);
|
||||||
if (resp.getStatus() == Response.Status.BAD_REQUEST.getStatusCode()) {
|
if (resp.getStatus() == Response.Status.BAD_REQUEST.getStatusCode()) {
|
||||||
|
|
|
@ -240,11 +240,14 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
||||||
.clientId("test-device")
|
.clientId("test-device")
|
||||||
.secret("secret")
|
.secret("secret")
|
||||||
.attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
.attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
||||||
|
.attribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+")
|
||||||
.build();
|
.build();
|
||||||
clients.add(app);
|
clients.add(app);
|
||||||
|
|
||||||
ClientRepresentation appPublic = ClientBuilder.create().id(KeycloakModelUtils.generateId()).publicClient()
|
ClientRepresentation appPublic = ClientBuilder.create().id(KeycloakModelUtils.generateId()).publicClient()
|
||||||
.clientId(DEVICE_APP_PUBLIC).attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
.clientId(DEVICE_APP_PUBLIC)
|
||||||
|
.attribute(OAuth2DeviceConfig.OAUTH2_DEVICE_AUTHORIZATION_GRANT_ENABLED, "true")
|
||||||
|
.attribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+")
|
||||||
.build();
|
.build();
|
||||||
clients.add(appPublic);
|
clients.add(appPublic);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
|
@ -844,4 +845,27 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
|
||||||
realmRep.getAttributes().remove(Constants.ACR_LOA_MAP);
|
realmRep.getAttributes().remove(Constants.ACR_LOA_MAP);
|
||||||
adminClient.realm("test").update(realmRep);
|
adminClient.realm("test").update(realmRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPostLogoutRedirectUri() throws Exception {
|
||||||
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
clientRep.setPostLogoutRedirectUris(Collections.singletonList("http://redirect/logout"));
|
||||||
|
OIDCClientRepresentation response = reg.oidc().create(clientRep);
|
||||||
|
assertEquals("http://redirect/logout", response.getPostLogoutRedirectUris().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPostLogoutRedirectUriPlus() throws Exception {
|
||||||
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
clientRep.setPostLogoutRedirectUris(Collections.singletonList("+"));
|
||||||
|
OIDCClientRepresentation response = reg.oidc().create(clientRep);
|
||||||
|
assertEquals("http://redirect", response.getPostLogoutRedirectUris().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPostLogoutRedirectUriNull() throws Exception {
|
||||||
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
OIDCClientRepresentation response = reg.oidc().create(clientRep);
|
||||||
|
assertNull(response.getPostLogoutRedirectUris());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.models.LDAPConstants;
|
||||||
import org.keycloak.models.credential.PasswordCredentialModel;
|
import org.keycloak.models.credential.PasswordCredentialModel;
|
||||||
import org.keycloak.models.credential.dto.PasswordCredentialData;
|
import org.keycloak.models.credential.dto.PasswordCredentialData;
|
||||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||||
|
@ -754,4 +755,14 @@ public class ExportImportUtil {
|
||||||
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE
|
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void testDefaultPostLogoutRedirectUris(RealmResource realm) {
|
||||||
|
for (ClientRepresentation client : realm.clients().findAll()) {
|
||||||
|
List<String> redirectUris = client.getRedirectUris();
|
||||||
|
if(redirectUris != null && !redirectUris.isEmpty()) {
|
||||||
|
String postLogoutRedirectUris = client.getAttributes().get(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS);
|
||||||
|
Assert.assertEquals("+", postLogoutRedirectUris);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,6 +315,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
testRealmDefaultClientScopes(migrationRealm);
|
testRealmDefaultClientScopes(migrationRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void testMigrationTo19_0_0() {
|
||||||
|
testPostLogoutRedirectUrisSet(migrationRealm);
|
||||||
|
}
|
||||||
|
|
||||||
protected void testDeleteAccount(RealmResource realm) {
|
protected void testDeleteAccount(RealmResource realm) {
|
||||||
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||||
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
||||||
|
@ -728,6 +732,11 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
ExportImportUtil.testClientDefaultClientScopes(realm);
|
ExportImportUtil.testClientDefaultClientScopes(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void testPostLogoutRedirectUrisSet(RealmResource realm) {
|
||||||
|
log.info("Testing that POST_LOGOUT_REDIRECT_URI is set to '+' for all clients in " + realm.toRepresentation().getRealm());
|
||||||
|
ExportImportUtil.testDefaultPostLogoutRedirectUris(realm);
|
||||||
|
}
|
||||||
|
|
||||||
private void testOfflineScopeAddedToClient() {
|
private void testOfflineScopeAddedToClient() {
|
||||||
log.infof("Testing offline_access optional scope present in realm %s for client migration-test-client", migrationRealm.toRepresentation().getRealm());
|
log.infof("Testing offline_access optional scope present in realm %s for client migration-test-client", migrationRealm.toRepresentation().getRealm());
|
||||||
|
|
||||||
|
@ -941,6 +950,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
||||||
testMigrationTo18_0_0();
|
testMigrationTo18_0_0();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void testMigrationTo19_x() {
|
||||||
|
testMigrationTo19_0_0();
|
||||||
|
}
|
||||||
|
|
||||||
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
||||||
if (supportedAuthzServices) {
|
if (supportedAuthzServices) {
|
||||||
testDecisionStrategySetOnResourceServer();
|
testDecisionStrategySetOnResourceServer();
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigratedData(false);
|
testMigratedData(false);
|
||||||
testMigrationTo12_x(true);
|
testMigrationTo12_x(true);
|
||||||
testMigrationTo18_x();
|
testMigrationTo18_x();
|
||||||
|
testMigrationTo19_x();
|
||||||
|
|
||||||
// Always test offline-token login during migration test
|
// Always test offline-token login during migration test
|
||||||
testOfflineTokenLogin();
|
testOfflineTokenLogin();
|
||||||
|
@ -95,6 +96,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigrationTo9_x();
|
testMigrationTo9_x();
|
||||||
testMigrationTo12_x(true);
|
testMigrationTo12_x(true);
|
||||||
testMigrationTo18_x();
|
testMigrationTo18_x();
|
||||||
|
testMigrationTo19_x();
|
||||||
|
|
||||||
// Always test offline-token login during migration test
|
// Always test offline-token login during migration test
|
||||||
testOfflineTokenLogin();
|
testOfflineTokenLogin();
|
||||||
|
@ -114,6 +116,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigrationTo9_x();
|
testMigrationTo9_x();
|
||||||
testMigrationTo12_x(true);
|
testMigrationTo12_x(true);
|
||||||
testMigrationTo18_x();
|
testMigrationTo18_x();
|
||||||
|
testMigrationTo19_x();
|
||||||
|
|
||||||
// Always test offline-token login during migration test
|
// Always test offline-token login during migration test
|
||||||
testOfflineTokenLogin();
|
testOfflineTokenLogin();
|
||||||
|
@ -141,6 +144,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigrationTo9_x();
|
testMigrationTo9_x();
|
||||||
testMigrationTo12_x(false);
|
testMigrationTo12_x(false);
|
||||||
testMigrationTo18_x();
|
testMigrationTo18_x();
|
||||||
|
testMigrationTo19_x();
|
||||||
|
|
||||||
// Always test offline-token login during migration test
|
// Always test offline-token login during migration test
|
||||||
testOfflineTokenLogin();
|
testOfflineTokenLogin();
|
||||||
|
@ -161,6 +165,7 @@ public class MigrationTest extends AbstractMigrationTest {
|
||||||
testMigrationTo9_x();
|
testMigrationTo9_x();
|
||||||
testMigrationTo12_x(false);
|
testMigrationTo12_x(false);
|
||||||
testMigrationTo18_x();
|
testMigrationTo18_x();
|
||||||
|
testMigrationTo19_x();
|
||||||
|
|
||||||
// Always test offline-token login during migration test
|
// Always test offline-token login during migration test
|
||||||
testOfflineTokenLogin();
|
testOfflineTokenLogin();
|
||||||
|
|
|
@ -189,6 +189,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
directUntrustedPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
directUntrustedPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
directUntrustedPublic.setFullScopeAllowed(false);
|
directUntrustedPublic.setFullScopeAllowed(false);
|
||||||
directUntrustedPublic.addRedirectUri("*");
|
directUntrustedPublic.addRedirectUri("*");
|
||||||
|
directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
|
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
|
||||||
|
|
||||||
ClientModel directNoSecret = realm.addClient("direct-no-secret");
|
ClientModel directNoSecret = realm.addClient("direct-no-secret");
|
||||||
|
|
|
@ -55,7 +55,10 @@ import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
|
@ -80,6 +83,7 @@ import org.keycloak.testsuite.pages.PageUtils;
|
||||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||||
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||||
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
|
||||||
|
import org.keycloak.testsuite.util.ClientManager;
|
||||||
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
|
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
|
||||||
import org.keycloak.testsuite.util.Matchers;
|
import org.keycloak.testsuite.util.Matchers;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
@ -165,6 +169,43 @@ public class RPInitiatedLogoutTest extends AbstractTestRealmKeycloakTest {
|
||||||
assertCurrentUrlEquals(redirectUri + "&state=something");
|
assertCurrentUrlEquals(redirectUri + "&state=something");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void postLogoutRedirect() {
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = loginUser();
|
||||||
|
String sessionId = tokenResponse.getSessionState();
|
||||||
|
|
||||||
|
String redirectUri = APP_REDIRECT_URI + "?post_logout";
|
||||||
|
|
||||||
|
List<String> postLogoutRedirectUris = Collections.singletonList(redirectUri);
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("test-app").setPostLogoutRedirectUri(postLogoutRedirectUris);
|
||||||
|
|
||||||
|
String idTokenString = tokenResponse.getIdToken();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(redirectUri).idTokenHint(idTokenString).build();
|
||||||
|
driver.navigate().to(logoutUrl);
|
||||||
|
|
||||||
|
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, redirectUri).assertEvent();
|
||||||
|
MatcherAssert.assertThat(false, is(isSessionActive(sessionId)));
|
||||||
|
|
||||||
|
assertCurrentUrlEquals(redirectUri);
|
||||||
|
|
||||||
|
tokenResponse = loginUser();
|
||||||
|
String sessionId2 = tokenResponse.getSessionState();
|
||||||
|
idTokenString = tokenResponse.getIdToken();
|
||||||
|
assertNotEquals(sessionId, sessionId2);
|
||||||
|
|
||||||
|
// Test also "state" parameter is included in the URL after logout. Make sure to use idTokenHint from the last login to match with current browser session
|
||||||
|
logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(redirectUri).idTokenHint(idTokenString).state("something").build();
|
||||||
|
driver.navigate().to(logoutUrl);
|
||||||
|
events.expectLogout(sessionId2).detail(Details.REDIRECT_URI, redirectUri).assertEvent();
|
||||||
|
MatcherAssert.assertThat(false, is(isSessionActive(sessionId2)));
|
||||||
|
assertCurrentUrlEquals(redirectUri + "&state=something");
|
||||||
|
} finally {
|
||||||
|
postLogoutRedirectUris = Collections.singletonList("+");
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("test-app").setPostLogoutRedirectUri(postLogoutRedirectUris);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void logoutRedirectWithIdTokenHintPointToDifferentSession() {
|
public void logoutRedirectWithIdTokenHintPointToDifferentSession() {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.findProtocolMapperByName;
|
import static org.keycloak.testsuite.admin.ApiUtil.findProtocolMapperByName;
|
||||||
|
@ -162,6 +163,12 @@ public class ClientManager {
|
||||||
clientResource.update(app);
|
clientResource.update(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPostLogoutRedirectUri(List<String> postLogoutRedirectUris) {
|
||||||
|
ClientRepresentation app = clientResource.toRepresentation();
|
||||||
|
OIDCAdvancedConfigWrapper.fromClientRepresentation(app).setPostLogoutRedirectUris(postLogoutRedirectUris);
|
||||||
|
clientResource.update(app);
|
||||||
|
}
|
||||||
|
|
||||||
public ClientManagerBuilder addWebOrigins(String... webOrigins) {
|
public ClientManagerBuilder addWebOrigins(String... webOrigins) {
|
||||||
ClientRepresentation app = clientResource.toRepresentation();
|
ClientRepresentation app = clientResource.toRepresentation();
|
||||||
if (app.getWebOrigins() == null) {
|
if (app.getWebOrigins() == null) {
|
||||||
|
|
Loading…
Reference in a new issue