KEYCLOAK-15327 backchannel logout invalidate offline session even if there is no corresponding active session found

This commit is contained in:
Benjamin Weimer 2020-08-27 17:08:37 +02:00 committed by Pedro Igor
parent 4e9bdd44f3
commit b2934e8dd0
4 changed files with 77 additions and 7 deletions

View file

@ -831,6 +831,17 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return getUserSession(realm, userSessionId, true); return getUserSession(realm, userSessionId, true);
} }
@Override
public UserSessionModel getOfflineUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
List<UserSessionModel> userSessions = getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerSessionId(brokerSessionId), true);
return userSessions.isEmpty() ? null : userSessions.get(0);
}
@Override
public List<UserSessionModel> getOfflineUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerUserId(brokerUserId), true);
}
@Override @Override
public void removeOfflineUserSession(RealmModel realm, UserSessionModel userSession) { public void removeOfflineUserSession(RealmModel realm, UserSessionModel userSession) {
UserSessionEntity userSessionEntity = getUserSessionEntity(realm, userSession, true); UserSessionEntity userSessionEntity = getUserSessionEntity(realm, userSession, true);

View file

@ -86,6 +86,8 @@ public interface UserSessionProvider extends Provider {
/** Will automatically attach newly created offline client session to the offlineUserSession **/ /** Will automatically attach newly created offline client session to the offlineUserSession **/
AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession, UserSessionModel offlineUserSession); AuthenticatedClientSessionModel createOfflineClientSession(AuthenticatedClientSessionModel clientSession, UserSessionModel offlineUserSession);
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user); List<UserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel user);
UserSessionModel getOfflineUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
List<UserSessionModel> getOfflineUserSessionByBrokerUserId(RealmModel realm, String brokerUserId);
long getOfflineSessionsCount(RealmModel realm, ClientModel client); long getOfflineSessionsCount(RealmModel realm, ClientModel client);
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max); List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);
@ -95,4 +97,5 @@ public interface UserSessionProvider extends Provider {
void close(); void close();
} }

View file

@ -320,14 +320,26 @@ public class LogoutEndpoint {
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm,
identityProviderAlias + "." + sessionId); identityProviderAlias + "." + sessionId);
if (logoutOfflineSessions) {
logoutOfflineUserSession(identityProviderAlias + "." + sessionId);
}
if (userSession != null) { if (userSession != null) {
backchannelLogoutResponse = logoutUserSession(userSession, logoutOfflineSessions); backchannelLogoutResponse = logoutUserSession(userSession);
} }
} }
return backchannelLogoutResponse; return backchannelLogoutResponse;
} }
private void logoutOfflineUserSession(String brokerSessionId) {
UserSessionModel offlineUserSession =
session.sessions().getOfflineUserSessionByBrokerSessionId(realm, brokerSessionId);
if (offlineUserSession != null) {
new UserSessionManager(session).revokeOfflineUserSession(offlineUserSession);
}
}
private BackchannelLogoutResponse backchannelLogoutFederatedUserId(String federatedUserId, private BackchannelLogoutResponse backchannelLogoutFederatedUserId(String federatedUserId,
List<String> identityProviderAliases, boolean logoutOfflineSessions) { List<String> identityProviderAliases, boolean logoutOfflineSessions) {
BackchannelLogoutResponse backchannelLogoutResponse = new BackchannelLogoutResponse(); BackchannelLogoutResponse backchannelLogoutResponse = new BackchannelLogoutResponse();
@ -336,9 +348,13 @@ public class LogoutEndpoint {
List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm,
identityProviderAlias + "." + federatedUserId); identityProviderAlias + "." + federatedUserId);
if (logoutOfflineSessions) {
logoutOfflineUserSessions(identityProviderAlias + "." + federatedUserId);
}
for (UserSessionModel userSession : userSessions) { for (UserSessionModel userSession : userSessions) {
BackchannelLogoutResponse userBackchannelLogoutResponse; BackchannelLogoutResponse userBackchannelLogoutResponse;
userBackchannelLogoutResponse = logoutUserSession(userSession, logoutOfflineSessions); userBackchannelLogoutResponse = logoutUserSession(userSession);
backchannelLogoutResponse.setLocalLogoutSucceeded(backchannelLogoutResponse.getLocalLogoutSucceeded() backchannelLogoutResponse.setLocalLogoutSucceeded(backchannelLogoutResponse.getLocalLogoutSucceeded()
&& userBackchannelLogoutResponse.getLocalLogoutSucceeded()); && userBackchannelLogoutResponse.getLocalLogoutSucceeded());
userBackchannelLogoutResponse.getClientResponses() userBackchannelLogoutResponse.getClientResponses()
@ -349,11 +365,19 @@ public class LogoutEndpoint {
return backchannelLogoutResponse; return backchannelLogoutResponse;
} }
private BackchannelLogoutResponse logoutUserSession(UserSessionModel userSession, boolean logoutOfflineSessions) { private void logoutOfflineUserSessions(String brokerUserId) {
BackchannelLogoutResponse backchannelLogoutResponse = List<UserSessionModel> offlineUserSessions =
AuthenticationManager.backchannelLogout(session, realm, userSession, session.sessions().getOfflineUserSessionByBrokerUserId(realm, brokerUserId);
session.getContext().getUri(),
clientConnection, headers, false, logoutOfflineSessions); UserSessionManager userSessionManager = new UserSessionManager(session);
for (UserSessionModel offlineUserSession : offlineUserSessions) {
userSessionManager.revokeOfflineUserSession(offlineUserSession);
}
}
private BackchannelLogoutResponse logoutUserSession(UserSessionModel userSession) {
BackchannelLogoutResponse backchannelLogoutResponse = AuthenticationManager.backchannelLogout(session, realm,
userSession, session.getContext().getUri(), clientConnection, headers, false);
if (backchannelLogoutResponse.getLocalLogoutSucceeded()) { if (backchannelLogoutResponse.getLocalLogoutSucceeded()) {
event.user(userSession.getUser()) event.user(userSession.getUser())

View file

@ -578,6 +578,38 @@ public class BackchannelLogoutTest extends AbstractNestedBrokerTest {
sessionIdProviderRealm); sessionIdProviderRealm);
} }
@Test
public void postBackchannelLogoutNestedBrokeringRevokeOfflineSessionsWithoutActiveUserSession() throws Exception {
String consumerClientId =
getClientId(nbc.consumerRealmName(), OidcBackchannelLogoutBrokerConfiguration.CONSUMER_CLIENT_ID);
subConsumerIdpRequestsOfflineSessions();
logInAsUserInNestedIDPForFirstTime();
String userIdConsumerRealm = getUserIdConsumerRealm();
String sessionIdProviderRealm = assertProviderLoginEventIdpClient(userIdProviderRealm);
String sessionIdConsumerRealm = assertConsumerLoginEvent(userIdConsumerRealm,
OidcBackchannelLogoutBrokerConfiguration.CONSUMER_CLIENT_ID);
assertActiveSessionInClient(nbc.consumerRealmName(), consumerClientId, userIdConsumerRealm,
sessionIdConsumerRealm);
logoutFromRealm(getConsumerRoot(), nbc.consumerRealmName());
assertNoSessionsInClient(nbc.consumerRealmName(), consumerClientId, userIdConsumerRealm,
sessionIdConsumerRealm);
assertActiveOfflineSessionInClient(nbc.consumerRealmName(), consumerClientId, userIdConsumerRealm,
sessionIdConsumerRealm);
String logoutTokenEncoded = getLogoutTokenEncodedAndSigned(userIdProviderRealm, sessionIdProviderRealm, true);
oauth.realm(nbc.consumerRealmName());
try (CloseableHttpResponse response = oauth.doBackchannelLogout(logoutTokenEncoded)) {
assertThat(response, Matchers.statusCodeIsHC(Response.Status.OK));
}
assertNoOfflineSessionsInClient(nbc.consumerRealmName(), consumerClientId, userIdConsumerRealm,
sessionIdConsumerRealm);
}
private void subConsumerIdpRequestsOfflineSessions() { private void subConsumerIdpRequestsOfflineSessions() {
IdentityProviderResource subConsumerIDPResource = adminClient.realm(nbc.subConsumerRealmName()) IdentityProviderResource subConsumerIDPResource = adminClient.realm(nbc.subConsumerRealmName())
.identityProviders().get(nbc.getSubConsumerIDPDisplayName()); .identityProviders().get(nbc.getSubConsumerIDPDisplayName());