From 990a5a4045931b3f48fb16df66f0785c335e90f1 Mon Sep 17 00:00:00 2001 From: Nithin Shekar Kuruba Date: Sun, 2 Jun 2024 11:14:53 -0700 Subject: [PATCH] feat: add user session removed ext --- .../authenticators/UserSessionRemover.java | 72 ++++++++++++++++++ .../UserSessionRemoverFactory.java | 76 +++++++++++++++++++ ...ycloak.authentication.AuthenticatorFactory | 1 + 3 files changed, 149 insertions(+) create mode 100644 docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemover.java create mode 100644 docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemoverFactory.java diff --git a/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemover.java b/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemover.java new file mode 100644 index 00000000..fc4c27dc --- /dev/null +++ b/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemover.java @@ -0,0 +1,72 @@ +package com.github.bcgov.keycloak.authenticators; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.Authenticator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.UserSessionProvider; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.sessions.AuthenticationSessionModel; + +import java.util.Map; + +public class UserSessionRemover implements Authenticator { + + private static final Logger logger = Logger.getLogger(UserSessionRemover.class); + + @Override + public boolean requiresUser() { + return false; + } + + @Override + public void authenticate(AuthenticationFlowContext context) { + AuthenticationSessionModel session = context.getAuthenticationSession(); + AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie( + context.getSession(), + context.getRealm(), + true + ); + + // 1. If no Cookie session, proceed to next step + if (authResult == null) { + context.attempted(); + return; + } + + // Need to use the KeycloakSession context to get the authenticating client ID. Not available on the AuthenticationFlowContext. + KeycloakSession keycloakSession = context.getSession(); + String authenticatingClientUUID = keycloakSession.getContext().getClient().getId(); + + // Get all existing sessions. If any session is associated with a different client, clear all user sessions. + UserSessionProvider userSessionProvider = keycloakSession.sessions(); + Map activeClientSessionStats = userSessionProvider.getActiveClientSessionStats(context.getRealm(), false); + + for (String activeSessionClientUUID : activeClientSessionStats.keySet()) { + if (!activeSessionClientUUID.equals(authenticatingClientUUID)) { + userSessionProvider.removeUserSession(context.getRealm(), authResult.getSession()); + } + } + + context.attempted(); + } + + @Override + public void action(AuthenticationFlowContext context) { + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return true; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + } + + @Override + public void close() { + } +} diff --git a/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemoverFactory.java b/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemoverFactory.java new file mode 100644 index 00000000..8194f68c --- /dev/null +++ b/docker/keycloak/extensions-24/services/src/main/java/com/github/bcgov/keycloak/authenticators/UserSessionRemoverFactory.java @@ -0,0 +1,76 @@ +package com.github.bcgov.keycloak.authenticators; + +import java.util.List; +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel.Requirement; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +public class UserSessionRemoverFactory implements AuthenticatorFactory { + + protected static final Requirement[] REQUIREMENT_CHOICES = { + Requirement.REQUIRED, Requirement.ALTERNATIVE, Requirement.DISABLED + }; + + private static final Authenticator AUTHENTICATOR_INSTANCE = new UserSessionRemover(); + + @Override + public String getId() { + return "user-session-remover"; + } + + @Override + public String getDisplayType() { + return "User Session Remover"; + } + + @Override + public String getHelpText() { + return "Checks if the user session is realted to any other client, and removes it if so."; + } + + @Override + public Authenticator create(KeycloakSession session) { + return AUTHENTICATOR_INSTANCE; + } + + @Override + public Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public List getConfigProperties() { + return null; + } + + @Override + public String getReferenceCategory() { + return null; + } + + @Override + public boolean isConfigurable() { + return false; + } + + @Override + public boolean isUserSetupAllowed() { + return true; + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } +} diff --git a/docker/keycloak/extensions-24/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/docker/keycloak/extensions-24/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 8ab8ef6c..e56d03de 100644 --- a/docker/keycloak/extensions-24/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/docker/keycloak/extensions-24/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -2,6 +2,7 @@ com.github.bcgov.keycloak.authenticators.IdentityProviderStopAuthenticatorFactor com.github.bcgov.keycloak.authenticators.CookieStopAuthenticatorFactory com.github.bcgov.keycloak.authenticators.ClientLoginAuthenticatorFactory com.github.bcgov.keycloak.authenticators.ClientLoginRoleBindingFactory +com.github.bcgov.keycloak.authenticators.UserSessionRemoverFactory com.github.bcgov.keycloak.authenticators.UserAttributeAuthenticatorFactory com.github.bcgov.keycloak.authenticators.broker.IdpDeleteUserIfDuplicateAuthenticatorFactory com.github.bcgov.keycloak.authenticators.browser.IdentityProviderStopFormFactory