Do not use offline sessions in the logout endpoint

Closes #46379

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc
2026-02-17 08:44:55 +01:00
committed by Pedro Igor
parent 2a7495b4f5
commit 9c6cf57410
3 changed files with 53 additions and 6 deletions
@@ -412,10 +412,6 @@ public class LogoutEndpoint {
try {
userSession = session.sessions().getUserSession(realm, userSessionIdFromIdToken);
if (userSession == null) {
userSession = session.sessions().getOfflineUserSession(realm, userSessionIdFromIdToken);
}
if (userSession == null) {
event.event(EventType.LOGOUT);
event.error(Errors.SESSION_EXPIRED);
@@ -308,15 +308,20 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
return contextRoot + "/auth/realms/" + realmName + "/account";
}
protected String getLoginUrl(String contextRoot, String realmName, String clientId) {
return getLoginUrl(contextRoot, realmName, clientId, "openid");
}
/**
* Get the login page for an existing client in provided realm
*
* @param contextRoot server base url without /auth
* @param realmName Name of the realm
* @param clientId ClientId of a client. Client has to exists in the realm.
* @param scope The scope parameter for the request
* @return Login URL
*/
protected String getLoginUrl(String contextRoot, String realmName, String clientId) {
protected String getLoginUrl(String contextRoot, String realmName, String clientId, String scope) {
List<ClientRepresentation> clients = adminClient.realm(realmName).clients().findByClientId(clientId);
assertThat(clients, Matchers.is(Matchers.not(Matchers.empty())));
@@ -327,7 +332,7 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
}
return contextRoot + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" +
clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=openid";
clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=" + scope;
}
protected void logoutFromRealm(String contextRoot, String realm) {
@@ -1,5 +1,6 @@
package org.keycloak.testsuite.broker;
import java.io.IOException;
import java.util.Map;
import org.keycloak.TokenVerifier;
@@ -8,7 +9,10 @@ import org.keycloak.models.IdentityProviderSyncMode;
import org.keycloak.representations.IDToken;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import org.keycloak.testsuite.util.oauth.IntrospectionResponse;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
@@ -18,6 +22,8 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class KcOidcBrokerLogoutFrontChannelTest extends AbstractKcOidcBrokerLogoutTest {
@Rule public AssertEvents events = new AssertEvents(this);
@@ -75,4 +81,44 @@ public class KcOidcBrokerLogoutFrontChannelTest extends AbstractKcOidcBrokerLogo
waitForPage(driver, "sign in to provider", true);
}
@Test
public void logoutIdPWithOfflineSession() throws IOException {
// login with offline_access in the application using IdP
driver.navigate().to(getLoginUrl(getConsumerRoot(), bc.consumerRealmName(), "broker-app", "openid offline_access"));
logInWithBroker(bc);
updateAccountInformation();
// Exchange code from "broker-app" client of "consumer" realm for the tokens
String code = oauth.parseLoginResponse().getCode();
AccessTokenResponse response = oauth.realm(bc.consumerRealmName())
.client("broker-app", "broker-app-secret")
.redirectUri(getConsumerRoot() + "/auth/realms/" + REALM_CONS_NAME + "/app")
.doAccessTokenRequest(code);
assertEquals(200, response.getStatusCode());
MatcherAssert.assertThat(response.getScope(), Matchers.containsString("offline_access"));
String idTokenString = response.getIdToken();
executeLogoutFromRealm(
getConsumerRoot(),
bc.consumerRealmName(),
"something-else",
idTokenString,
null,
null
);
infoPage.assertCurrent();
assertEquals("You are logged out", infoPage.getInfo());
// the offline session should be maintained
IntrospectionResponse introspectionResponse = oauth.doIntrospectionAccessTokenRequest(response.getAccessToken());
assertEquals(200, introspectionResponse.getStatusCode());
assertTrue(introspectionResponse.asJsonNode().get("active").asBoolean());
// the external IdP online session should be also maintained, re-authenticate
driver.navigate().to(getLoginUrl(getConsumerRoot(), bc.consumerRealmName(), "broker-app"));
logInAsUserInIDPWithReAuthenticate();
assertNotNull(oauth.parseLoginResponse().getCode());
}
}