UserSessionLimitsAuthenticatorFactory.java

package org.keycloak.authentication.authenticators.sessionlimits;

import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.Arrays;
import java.util.List;

public class UserSessionLimitsAuthenticatorFactory implements AuthenticatorFactory {
    public static final String USER_REALM_LIMIT = "userRealmLimit";
    public static final String USER_CLIENT_LIMIT = "userClientLimit";
    public static final String BEHAVIOR = "behavior";
    public static final String DENY_NEW_SESSION = "Deny new session";
    public static final String TERMINATE_OLDEST_SESSION = "Terminate oldest session";
    public static final String USER_SESSION_LIMITS = "user-session-limits";
    public static final String ERROR_MESSAGE = "errorMessage";

    private static AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
            AuthenticationExecutionModel.Requirement.REQUIRED,
            AuthenticationExecutionModel.Requirement.DISABLED
    };

    @Override
    public String getDisplayType() {
        return "User session count limiter";
    }

    @Override
    public String getReferenceCategory() {
        return null;
    }

    @Override
    public boolean isConfigurable() {
        return true;
    }

    @Override
    public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
        return REQUIREMENT_CHOICES;
    }

    @Override
    public boolean isUserSetupAllowed() {
        return false;
    }

    @Override
    public String getHelpText() {
        return "Configures how many concurrent sessions a single user is allowed to create for this realm and/or client";
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        ProviderConfigProperty userRealmLimit = new ProviderConfigProperty();
        userRealmLimit.setName(USER_REALM_LIMIT);
        userRealmLimit.setLabel("Maximum concurrent sessions for each user within this realm.");
        userRealmLimit.setHelpText("Provide a zero or negative value to disable this limit.");
        userRealmLimit.setType(ProviderConfigProperty.STRING_TYPE);
        userRealmLimit.setDefaultValue("3");

        ProviderConfigProperty userClientLimit = new ProviderConfigProperty();
        userClientLimit.setName(USER_CLIENT_LIMIT);
        userClientLimit.setLabel("Maximum concurrent sessions for each user per keycloak client.");
        userClientLimit.setHelpText("Provide a zero or negative value to disable this limit. In case a limit for the realm is enabled, specify this value below the total realm limit.");
        userClientLimit.setType(ProviderConfigProperty.STRING_TYPE);
        userClientLimit.setDefaultValue("0");
        
        ProviderConfigProperty behaviourProperty = new ProviderConfigProperty();
        behaviourProperty.setName(BEHAVIOR);
        behaviourProperty.setLabel("Behavior when user session limit is exceeded");
        behaviourProperty.setType(ProviderConfigProperty.LIST_TYPE);
        behaviourProperty.setDefaultValue(DENY_NEW_SESSION);
        behaviourProperty.setOptions(Arrays.asList(DENY_NEW_SESSION, TERMINATE_OLDEST_SESSION));
        
        ProviderConfigProperty customErrorMessage = new ProviderConfigProperty();
        customErrorMessage.setName(ERROR_MESSAGE);
        customErrorMessage.setLabel("Optional custom error message");
        customErrorMessage.setHelpText("If left empty a default error message is shown");
        customErrorMessage.setType(ProviderConfigProperty.STRING_TYPE);

        return Arrays.asList(userRealmLimit, userClientLimit, behaviourProperty, customErrorMessage);
    }

    @Override
    public Authenticator create(KeycloakSession keycloakSession) {
        return new UserSessionLimitsAuthenticator(keycloakSession);
    }

    @Override
    public void init(Config.Scope scope) {

    }

    @Override
    public void postInit(KeycloakSessionFactory keycloakSessionFactory) {

    }

    @Override
    public void close() {

    }

    @Override
    public String getId() {
        return USER_SESSION_LIMITS;
    }
}