Conditional Authenticator Personnalisé Avec Keycloak

Dans Keycloak, quand vous avez besoin de personnalisé un flow de connexion suivant certaines conditions non prévue de base par Keycloak (User configured et User Attribute), la manière de procéder est d’implémenter un ConditionalAuthenticator

Prérequis

Nous allons avoir besoin des dépendances suivantes :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi</artifactId>
</dependency>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-server-spi-private</artifactId>
</dependency>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-services</artifactId>
</dependency>

Création de la factory

Keycloak utilise le mécanisme Java standard des SPIs pour définir ses providers. Nous devons donc créer notre factory permettant à Keycloak d’initialiser notre provider.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class CustomConditionalAuthenticatorFactory implements ConditionalAuthenticationFactory {

	public static final String PROVIDER_ID= "custom-authenticator";
	
	public static final String CONFIG_CONDITION = "foo";
	
	private static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
                AuthenticationExecutionModel.Requirement.REQUIRED,
                AuthenticationExecutionModel.Requirement.DISABLED
        };
        
        Override
        public void init(Config.Scope scope) {}

        @Override
        public void postInit(KeycloakSessionFactory keycloakSessionFactory) {}

        @Override
        public void close() {}

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

        @Override
        public String getDisplayType() {
                return "Condition - Custom Authenticator";
        }

        @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 "Flow is executed if condition is validated.";
        }

        @Override
        public List<ProviderConfigProperty> getConfigProperties() {
                ProviderConfigProperty condition = new ProviderConfigProperty();
                condition.setName(CONFIG_CONDITION);
                condition.setLabel("Custom Condition");
                condition.setHelpText("Set a value to be validated");
                condition.setType(STRING_TYPE);
                condition.setDefaultValue("");
        }

        @Override
        public ConditionalAuthenticator getSingleton() {
                return CustomConditionalAuthenticator.SINGLETON;
        }
}

Nombre de méthodes ne sont pas utilisées pour un authenticator conditionnel. Notre factory est en charge de l’instanciation du provider et de la gestion de la configuration dans l’interface de Keycloak. Dans notre cas, nous avons une condition de type String ayant pour nom foo. Notre factory définie aussi le type d’exécution possible. Ici, nous laisser le choix pour l’état Disabled ou Required. La méthode getConfigurationProvider est utilisée pour permettre la configuration du provider depuis l’interface d’administration de Keycloak.

Création du provider

Le provider est la classe contenant la logique de notre implémentation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CustonContidionalAuthenticator implements ConditionalAuthenticator {

    private static final Logger logger = Logger.getLogger(ThreatmetrixConditionalAuthenticator.class);
    
    @Override
    public boolean matchCondition(AuthenticationFlowContext context ) {
    	Map<String, String> config = context.getAuthenticatorConfig().getConfig();
    	if (!config.containsKey(CustomConditionalAuthenticatorFactory.CONFIG_CONDITION)) {
            throw new AuthenticationFlowException(AuthenticationFlowError.valueOf("Missing url for Threatmetrix configuration."));
        }
        String condition = config.get(CustomConditionalAuthenticatorFactory.CONFIG_CONDITION);
        
	return evaluate(condition)
    }
    
    @Override
    public void action(AuthenticationFlowContext authenticationFlowContext) {}

    @Override
    public boolean requiresUser() {}

    @Override
    public void setRequiredActions(KeycloakSession keycloakSession, RealmModel realmModel, UserModel userModel) {}

    @Override
    public void close() {}

matchCondition est la seule méthode utilisée. Elle permet d’évaluer si la condition sera positive ou négative. Pour simplifier le code, notre implémentation appelle la méthode evaluate en charge d’évaluer notre condition. C’est ici que sera définir le traitement souhaité pour l’implémentation.

Référencement de notre provider dans Keycoak

Il ne nous reste plus qu’à référencer notre provider dans Keycloak en créant le fichier resources/META-INF/services/org.keycloak.authentication.AuthenticationFactory avec le FQDN de notre factory.

1
org.techland.keycloak.provider.CustomConditionalAuthenticatorFactory

Conclusion

Le création d’un authenticator personnalisé est simple et ne demande que l’implémentation de deux classes, la factory et le provider. Une fois le jar créé, il suffit de le déposer dans le dossier /opt/keycloak/providers puis de se rendre dans le menu Authentication et sélectionner un flow personnalisé pour ajouter notre nouvelle condition.

0%