Points d'extension et coût de maintenance

Connaître les SPI les plus utilisés (authenticator, required action, user storage, event listener, protocol mapper), le rythme des versions de Keycloak et ce que chaque extension coûte à chaque montée de version.

Approfondir Cours 16 sur 17 de Keycloak Academy 40 min 4 questions de quiz Cours suivant : API d'administration et comptes de service

Sur cette page
  1. Le mécanisme des SPI
  2. Les cinq SPI à connaître
  3. À quoi ressemble le code
  4. Paramétrer d’abord
  5. Le rythme des versions
  6. Le Upgrading Guide
  7. Les risques et comment les réduire
  8. À retenir pour l’architecture
  9. Natif, extension ou hébergeur
  10. Quiz

Keycloak se prolonge par du code Java : de nouveaux facteurs d’authentification, une base d’utilisateurs existante, un format de jeton imposé. Chaque extension résout un besoin, puis coûte à chaque montée de version. Ce cours donne les repères pour arbitrer entre paramétrage et extension, et pour chiffrer la maintenance de ce qui est retenu.

Le mécanisme des SPI

Une SPI (Service Provider Interface) est un point d’extension. L’implémenter demande trois éléments :

  • une factory (…ProviderFactory), instanciée une seule fois, qui lit la configuration et porte l’identifiant du provider ;
  • un provider, créé par la factory à chaque requête, qui fait le travail ;
  • un fichier META-INF/services/<interface de la factory> qui déclare la factory.

Le tout est livré dans un JAR déposé dans providers/. En mode optimisé, une commande kc.sh build intègre les providers ; le mode dev le fait au démarrage. La console du realm master liste tous les providers chargés (menu utilisateur, Realm info, onglet Provider info).

Le projet Maven importe la nomenclature keycloak-parent de la version visée. Le code est donc lié à une version précise de Keycloak.

Les cinq SPI à connaître

SPIÀ quoi elle sertExemple de besoinStabilité annoncée
AuthenticatorAjouter une étape à un flux de connexionVérifier un code reçu par un canal maison, interroger un service de risqueInterne
Required actionImposer une action à l’utilisateur après connexionAccepter une charte, compléter un attributInterne (fait partie de l’Authentication SPI)
User storageBrancher une source d’utilisateurs externeBase applicative existante, migration au fil de l’eauPas de mention interne
Event listenerRéagir aux événementsPousser vers un SIEM ou un busInterne
Protocol mapperCalculer un claim ou un attribut SAMLClaim dérivé d’un référentiel externePas de chapitre SPI dédié ; dépend de classes du serveur

Pour l’Authentication SPI comme pour l’Event Listener SPI, la documentation indique qu’il s’agit d’une SPI interne : stable jusqu’ici, mais susceptible de changer à n’importe quelle version sans préavis. C’est l’information la plus importante pour chiffrer la maintenance.

À quoi ressemble le code

Un authenticator qui refuse les comptes suspendus :

public class StatutAuthenticator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        UserModel user = context.getUser();
        if ("suspendu".equals(user.getFirstAttribute("statut"))) {
            context.failure(AuthenticationFlowError.USER_DISABLED);
            return;
        }
        context.success();
    }

    @Override
    public void action(AuthenticationFlowContext context) {
    }

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

    @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() {
    }
}

Il faut y ajouter une AuthenticatorFactory (identifiant, libellé, exigences possibles, configuration), le fichier de services, des tests. Et surtout, ce besoin précis se paramètre sans code : un sous-flux conditionnel avec la condition sur attribut utilisateur, suivi de l’étape de refus d’accès. C’est l’objet du lab.

Un protocol mapper qui produit un claim dérivé d’un attribut :

public class CodeServiceMapper extends AbstractOIDCProtocolMapper
        implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {

    @Override
    protected void setClaim(IDToken token, ProtocolMapperModel mappingModel,
            UserSessionModel userSession, KeycloakSession session,
            ClientSessionContext clientSessionCtx) {
        String service = userSession.getUser().getFirstAttribute("service");
        token.getOtherClaims().put("code_service",
                service == null ? "INCONNU" : service.toUpperCase());
    }
}

La classe complète déclare aussi son identifiant, sa catégorie, son texte d’aide et ses propriétés de configuration. Là encore, un mapper d’attribut natif couvre le cas simple ; le code ne se justifie que pour un calcul ou un appel externe.

Paramétrer d’abord

BesoinRéponse native à essayerExtension si…
MFA selon le rôle ou l’applicationSous-flux conditionnels, niveaux d’authentification (ACR)Le critère vient d’un service externe
Refuser certains comptesCondition sur attribut ou rôle, refus d’accèsLa règle exige un appel externe
Claim spécifiqueMappers d’attribut, de rôle, de groupe, valeur codée en durLe claim se calcule ou vient d’ailleurs
Base d’utilisateurs existanteFédération LDAP, import avec empreintes PBKDF2 ou Argon2Base SQL ou API maison, format d’empreinte non lu nativement
Traces vers le SOCJournaux serveur en JSONFormat ou transport imposé
Charte graphiqueThème hérité d’un thème fourniPas d’extension Java : c’est un thème

Le rythme des versions

Les dates de publication de la branche 26 montrent une cadence régulière : 26.0 en octobre 2024, puis une version mineure environ tous les trois mois (26.1 en janvier 2025, 26.2 en avril, 26.3 en juillet, 26.4 fin septembre, 26.5 en janvier 2026, 26.6 en avril, 26.7 en juillet, 26.8 en octobre 2026). Entre deux, des versions correctives sortent toutes les deux à quatre semaines. Les versions mineures de la branche 26 ont apporté des fonctions majeures (organisations, permissions v2, SCIM) : « mineure » ne veut pas dire « sans impact ».

Le projet communautaire publie ses correctifs sur les versions récentes. Un support long sur une version figée relève d’une offre commerciale, ou de ce que l’hébergeur garantit : c’est à vérifier avec lui.

Le Upgrading Guide

Le Upgrading Guide décrit, version par version, ce qui change : Breaking changes, Notable changes, Deprecated features, Removed features. Le guide pose que les versions mineures et correctives n’introduisent de rupture que pour corriger un bug ; il en liste pourtant pour des versions correctives. Il faut donc le lire à chaque montée, correctifs compris.

La procédure générale : sauvegarder la base, arrêter, déployer la nouvelle version, laisser Keycloak migrer le schéma au démarrage, puis recompiler et redéployer extensions et thèmes contre la nouvelle version.

Les risques et comment les réduire

RisqueExempleParade
Changement d’une SPI interneSignature modifiée, classe déplacéeCode minimal, recompilation et tests à chaque version
Dépendance à des classes d’implémentationUsage de classes de keycloak-servicesS’en tenir aux interfaces documentées
Comportement modifié sans erreur de compilationUn événement change de nom ou de contenuTests d’intégration sur un vrai Keycloak (conteneur)
Thème décaléNouveau champ absent d’un gabarit réécritHériter plutôt que réécrire, tester chaque page
Montée de version bloquéeL’extension retarde l’application d’un correctif de sécuritéRecette des extensions préparée à l’avance sur les versions candidates

Pour chiffrer l’enveloppe, classez chaque extension : simple (mapper, listener sans état), moyenne (authenticator avec formulaire, required action), complexe (user storage, flux appelant des services externes). Multipliez ensuite l’effort de recette par le nombre de montées de version prévues par an, environ quatre mineures plus des correctifs.

Inventorier les SPI et préférer le paramétrage

30 min

Installer le lab
  1. Récupérez un jeton d’administration et listez les SPI chargées avec leur indicateur interne :

    KC=http://localhost:8080
    TOKEN=$(curl -s -d client_id=admin-cli -d username=admin -d password=admin \
      -d grant_type=password "$KC/realms/master/protocol/openid-connect/token" | jq -r .access_token)
    curl -s -H "Authorization: Bearer $TOKEN" "$KC/admin/serverinfo" \
      | jq -r '.providers | to_entries[] | "\(.key) internal=\(.value.internal)"' \
      | grep -Ei 'authenticator|required-action|storage|eventsListener|protocol-mapper'
  2. Notez quelles SPI sont marquées internes, puis listez les authenticators disponibles et repérez les conditions (conditional-…) et le refus d’accès :

    curl -s -H "Authorization: Bearer $TOKEN" "$KC/admin/serverinfo" \
      | jq -r '.providers.authenticator.providers | keys[]'
  3. Dans le realm approfondir, ouvrez Realm settings, onglet User profile, et créez un attribut statut modifiable par les administrateurs seulement.

  4. Dans Authentication, dupliquez le flux browser sous le nom browser-statut. Dans le sous-flux des formulaires, après le formulaire identifiant et mot de passe, ajoutez un sous-flux conditionnel contenant la condition sur attribut utilisateur (attribut statut, valeur attendue suspendu) puis l’étape de refus d’accès. Le sous-flux est en Conditional, la condition et le refus en Required. Les libellés exacts des étapes varient un peu selon la version.

  5. Liez browser-statut comme flux navigateur (Bind flow).

  6. Donnez à un utilisateur de test l’attribut statut à suspendu, puis connectez-vous avec lui sur http://localhost:8080/realms/approfondir/account : l’accès est refusé. Passez l’attribut à actif : l’accès est accordé.

  7. Remettez le flux browser d’origine comme flux navigateur. Rédigez en deux lignes pourquoi l’authenticator Java du cours n’est pas nécessaire ici.

À retenir pour l’architecture

Chaque extension doit passer par une question : le paramétrage natif suffit-il ? Si non, classez-la, notez la SPI utilisée et sa stabilité, et inscrivez sa recette dans le plan de montée de version. Vérifiez avec l’hébergeur ses conditions d’acceptation des extensions et son calendrier de montée de version : ce sont eux qui fixent, en pratique, le rythme de votre maintenance.

Natif, extension ou hébergeur

Natif : fourni par Keycloak, à configurer. Extension : demande du code à maintenir (un SPI, le mécanisme de plugin de Keycloak). Hébergeur : dépend de la plateforme qui héberge Keycloak, à vérifier avec elle.

CapacitéVerdictPrécision
Conditions de flux natives (attribut, rôle, facteur configuré) et refus d'accèsNatifCouvrent beaucoup de besoins d'authentification sans code. À vérifier avant toute extension.
Authenticator et required action sur mesureExtensionAuthentication SPI, classée interne par Keycloak : peut changer à chaque version.
User Storage SPI (base applicative, migration au fil de l'eau)ExtensionSPI documentée sans mention interne, plus stable, mais le code est le plus volumineux.
Protocol mapper sur mesureExtensionCode court, mais il dépend de classes d'implémentation du serveur.
Event listener sur mesureExtensionEvent Listener SPI, classée interne.
Déploiement d'extensions et calendrier des montées de versionHébergeurConditions d'acceptation, recette et délai de montée de version sont à convenir avec l'hébergeur.

Vérifier ce que vous avez retenu

4 questions

  1. Un besoin consiste à refuser la connexion aux comptes dont l'attribut statut vaut suspendu. Quelle est la première réponse d'architecte ?

  2. Deux extensions sont proposées : un user storage provider et un authenticator. Laquelle porte le plus fort risque de rupture lors d'une montée de version de Keycloak, à taille égale ?

  3. Keycloak publie une version mineure environ chaque trimestre et des correctifs toutes les quelques semaines. Qu'en déduire pour l'enveloppe de maintenance ?

  4. Pourquoi lire les sections Breaking changes du Upgrading Guide même pour une version de correctif ?