KEYCLOAK-15327 backchannel logout invalidate offline session even if there is no corresponding active session found
This commit is contained in:
parent
4e9bdd44f3
commit
b2934e8dd0
4 changed files with 77 additions and 7 deletions
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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());
|
||||||
|
|
Loading…
Reference in a new issue