TutorielGuides

Callbacks et génération de formulaire

Sur cette page

Ce tutoriel construit un graphe d’authentification minimal (nom d’utilisateur → mot de passe → vérification LDAP), puis explique le mécanisme des callbacks : comment TOSIAM décrit, de façon générique, l’information qu’il attend de l’utilisateur, et comment le client de connexion transforme automatiquement cette description en formulaire HTML.

Étape 1 : créer le graphe#

Le graphe le plus simple qui authentifie réellement un utilisateur enchaîne trois nœuds : collecte du nom d’utilisateur, collecte du mot de passe, vérification contre le User Store.

UsernameCollectorNode ──outcome──▶ PasswordCollectorNode ──outcome──▶ DataStoreNode ──true──▶ SuccessNode
                                                                            └──false──▶ FailureNode
json
{
  "startNodeId": "username",
  "steps": {
    "username": { "type": "UsernameCollectorNode", "config": {}, "outcomes": { "outcome": "password" } },
    "password": { "type": "PasswordCollectorNode", "config": {}, "outcomes": { "outcome": "check" } },
    "check": {
      "type": "DataStoreNode",
      "config": { "authLevel": 0 },
      "outcomes": { "true": "success", "false": "failure" }
    },
    "success": { "type": "SuccessNode", "config": {}, "outcomes": {} },
    "failure": { "type": "FailureNode", "config": {}, "outcomes": {} }
  }
}

Enregistrez ce contenu dans graphe-simple.json, à la racine du projet généré, puis créez le graphe via le goal ssoadm du plugin Maven :

bash
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="create-auth-graph --realm / --name simple --datafile $(pwd)/graphe-simple.json"

Étape 2 : exposer le graphe comme point d’entrée#

Un graphe seul n’est pas directement authentifiable : il faut d’abord l’instancier comme module d’authentification, puis l’exposer via une Authentication Configuration (l’équivalent TOSIAM d’une chaîne) du même nom, qui la référence.

bash
# 1. Instancier le module AuthGraph, en le pointant vers le graphe
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="create-auth-instance --realm / --name simple --authtype AuthGraph"
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="update-auth-instance --realm / --name simple --attributevalues tosiam-auth-graph-id=simple"

# 2. L'exposer via une configuration d'authentification du même nom
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="create-auth-cfg --realm / --name simple"
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="add-auth-cfg-entr --realm / --name simple --modulename simple --criteria REQUISITE"

Une dernière commande, optionnelle mais utile pour l’étape 5 : par défaut, un utilisateur non administrateur qui s’authentifie est redirigé vers la console legacy (/console/), qui le renvoie elle-même vers une page de profil minimale. Pour le rediriger plutôt vers l’application account générée par le plugin (module account du projet, voir Générer un projet avec Maven), configurez l’URL de dashboard du realm :

bash
mvn org.tst.tosiam:tosiam-project-generator-maven-plugin:3.37.0:ssoadm \
    -Dssoadm.args="add-svc-realm --realm / --servicename selfService --attributevalues selfServiceDashboardUrl=account"

Étape 3 : le concept de callback#

Le moteur de graphe (GraphEngine) ne sait rien afficher : quand un nœud a besoin d’une information, il retourne REQUEST_INPUT, et le serveur REST traduit ce besoin en un ou plusieurs callbacks, un concept hérité directement du mécanisme JAAS (javax.security.auth.callback) sur lequel TOSIAM est construit. Un callback ne décrit jamais du HTML : il déclare seulement un type (NameCallback, PasswordCallback, ChoiceCallback…), un output en lecture seule (typiquement un prompt à afficher) et un input vide, à remplir par le client puis à reposter tel quel.

Appelons directement l’API REST pour observer cet échange, sans passer par aucune interface. Trois en-têtes sont obligatoires sur ce endpoint : Content-Type, X-Requested-With (protection CSRF) et Accept-API-Version (sélection de version de l’API) :

bash
curl -s -X POST \
  "http://localhost:8080/tosiam/json/authenticate?authIndexType=service&authIndexValue=simple" \
  -H "Content-Type: application/json" \
  -H "X-Requested-With: XMLHttpRequest" \
  -H "Accept-API-Version: protocol=1.0,resource=2.0"
json
{
  "authId": "eyAidHlwIjogIkpXVCIsIC...",
  "template": "",
  "stage": "UsernameCollectorNode",
  "header": "",
  "callbacks": [
    {
      "type": "NameCallback",
      "output": [ { "name": "prompt", "value": "Username" } ],
      "input": [ { "name": "IDToken1", "value": "" } ]
    }
  ]
}

Le nœud UsernameCollectorNode s’est traduit en un unique callback NameCallback. authId identifie la conversation en cours (un JWT signé, à renvoyer intact à chaque étape) ; IDToken1 est le nom de champ que le serveur attend en retour, pas un nom métier comme username.

On répond en repostant le même JSON, authId inclus, avec input[0].value rempli, sur l’URL sans les paramètres de requête (la conversation est maintenant portée par authId) :

bash
curl -s -X POST "http://localhost:8080/tosiam/json/authenticate" \
  -H "Content-Type: application/json" \
  -H "X-Requested-With: XMLHttpRequest" \
  -H "Accept-API-Version: protocol=1.0,resource=2.0" \
  -d '{
    "authId": "eyAidHlwIjogIkpXVCIsIC...",
    "callbacks": [
      {
        "type": "NameCallback",
        "output": [ { "name": "prompt", "value": "Username" } ],
        "input": [ { "name": "IDToken1", "value": "demo" } ]
      }
    ]
  }'

Le graphe avance au nœud suivant et répond avec un nouveau callback, cette fois PasswordCallback :

json
{
  "authId": "eyAidHlwIjogIkpXVCIsIC...",
  "stage": "PasswordCollectorNode",
  "callbacks": [
    {
      "type": "PasswordCallback",
      "output": [ { "name": "prompt", "value": "Password" } ],
      "input": [ { "name": "IDToken1", "value": "" } ]
    }
  ]
}

En repostant ce callback rempli (demo / changeit), DataStoreNode vérifie les identifiants contre le User Store puis transitionne silencieusement (c’est un nœud de décision : il ne produit jamais de callback) vers SuccessNode. La réponse finale ne contient plus de callback :

json
{
  "tokenId": "fl82k0p9enspW4CCrGZkiJl9EMs.*AAJTSQACMDEAAlNLABxweFBDMEtFRmVOcFRtR042N2ZYVGs0eExSajg9AAJTMQAA*",
  "successUrl": "/console/"
}

accompagnée d’un cookie de session (iPlanetDirectoryPro, valeur = tokenId) : c’est la session TOSIAM.

Étape 4 : la génération automatique de formulaire#

C’est exactement ce même contrat JSON que consomme tosiam-authentication-ui, le client de connexion HTML/TS livré avec TOSIAM (module auth du projet généré, voir Générer un projet avec Maven). Il ne connaît jamais à l’avance la liste des nœuds d’un graphe donné : il se contente d’inspecter le champ type de chaque callback reçu, et de le confier à une fabrique qui choisit le bon élément de formulaire :

texte
Stage { callbacks: [...] }
   │
   ▼  pour chaque callback, selon callback.type
FormElementFactory.getFormElement(type, callbackIndex)
   │
   ├─ "NameCallback"     → DefaultCallback     (<input type="text" autocomplete="username">)
   ├─ "PasswordCallback" → PasswordCallback    (<input type="password"> + bouton afficher/masquer)
   ├─ "ChoiceCallback"   → ChoiceCallback      (liste de choix)
   ├─ ...
   └─ type inconnu       → DefaultCallback     (repli générique)

Chaque élément produit son propre fragment HTML (getHtml()), assemblé dans un <form> unique. À la soumission, le client relit la valeur saisie pour chaque champ, l’écrit dans callback.input[0].value, puis reposte le tableau callbacks complet sur /authenticate, soit exactement l’échange REST reproduit à l’étape précédente avec curl. Rien dans ce mécanisme générique ne fait référence à UsernameCollectorNode ou PasswordCollectorNode : ce sont uniquement les types de callback (NameCallback, PasswordCallback) qui pilotent le rendu.

Cela a une conséquence directe pour vos graphes personnalisés : tant qu’un nœud custom retourne un callback d’un type standard, il s’affiche automatiquement, sans aucun développement front-end. TOSIAM enregistre en plus, pour une poignée de nœuds qui ont besoin de plus qu’un simple champ (WebAuthn, QR code TOTP, reCAPTCHA…), des rendus spécifiques indexés sur le nom du nœud plutôt que sur le type de callback, mais même ceux-ci s’appuient sur le même mécanisme générique pour leurs champs de formulaire ordinaires. En l’absence de rendu spécifique, c’est ce mécanisme générique qui s’applique. C’est le cas de notre graphe simple.

Étape 5 : se connecter dans le navigateur#

Ouvrez http://localhost:8080/tosiam/auth/#login?service=simple (le realm racine / est celui par défaut, inutile de le préciser). Le formulaire nom d’utilisateur s’affiche, généré automatiquement à partir du callback NameCallback vu plus haut, sans aucune ligne de CSS ou de JS écrite pour ce graphe :

Formulaire de connexion TOSIAM généré automatiquement, champ Username

Saisissez demo, validez : le second callback (PasswordCallback) produit un champ masqué avec bouton afficher/masquer :

Formulaire de connexion TOSIAM généré automatiquement, champ Password

Saisissez changeit : DataStoreNode valide les identifiants, la session est ouverte, et, grâce au selfServiceDashboardUrl configuré à l’étape précédente, le navigateur est redirigé vers l’application account du projet généré :

Application account TOSIAM après authentification réussie

Mis à jour le