Double authentification par e-mail (SMTP)
Sur cette page
Ce tutoriel ajoute un second facteur à la connexion : après son mot de passe, l’utilisateur reçoit un code à six chiffres par e-mail et le saisit. Le code est tiré au hasard à chaque connexion et expire au bout de cinq minutes.
Le nœud EmailOtpSendNode n’a pas de réglage SMTP : il envoie le message par le service de messagerie du royaume (Email Service), qui désigne le serveur SMTP, le port, le chiffrement et l’adresse d’expédition. Pour suivre le tutoriel sans serveur de messagerie, un faux serveur SMTP, un petit programme Node.js, reçoit les e-mails et les affiche dans le navigateur.
Étape 1 : l’adresse e-mail#
TOSIAM lit l’adresse dans l’attribut mail du profil. Pour l’utilisateur dduck :
curl -X PUT "http://localhost:8080/tosiam/json/ref/users/dduck" \
-H "Content-Type: application/json" \
-H "X-Requested-With: XMLHttpRequest" \
-H "Accept-API-Version: resource=3.0, protocol=1.0" \
-H "iPlanetDirectoryPro: <jeton administrateur>" \
-d '{"mail": ["donald.duck@example.com"]}'Étape 2 : le faux serveur SMTP#
Téléchargez faux-smtp.mjs et lancez-le :
node faux-smtp.mjsIl écoute en SMTP sur le port 2525, sans chiffrement ni authentification, et accepte tous les messages sans les remettre. Sa boîte de réception est sur http://localhost:8025. Les variables SMTP_PORT et HTTP_PORT changent ces ports.
Étape 3 : le service de messagerie du royaume#
Un royaume neuf n’a pas de service de messagerie : sans lui, aucun nœud ne peut envoyer d’e-mail. Dans le royaume ref, ouvrez Services › Ajouter un service, choisissez Email Service (Service de messagerie), puis renseignez :
| Réglage | Valeur | Champ REST |
|---|---|---|
| Nom d’hôte du serveur de messagerie | localhost | hostname |
| Port du serveur de messagerie | 2525 | port |
| Connexion sécurisée au serveur de messagerie | Non SSL | sslState |
| Adresse d’expédition | TOSIAM <no-reply@example.com> | from |
ref : serveur (1), port (2), chiffrement (3) et adresse d’expédition (4).Laissez vides le nom d’utilisateur et le mot de passe : le faux serveur ne demande pas d’authentification.
Le même service se crée par l’API REST :
curl -X POST "http://localhost:8080/tosiam/json/ref/realm-config/services/email?_action=create" \
-H "Content-Type: application/json" \
-H "X-Requested-With: XMLHttpRequest" \
-H "Accept-API-Version: resource=1.0" \
-H "iPlanetDirectoryPro: <jeton administrateur>" \
-d '{"hostname": "localhost", "port": 2525, "sslState": "Non SSL", "from": "TOSIAM <no-reply@example.com>"}'Une fois le service créé, la même adresse modifie un réglage avec PUT (sans ?_action=create), par exemple {"port": 587}.
Le service de messagerie est réglé une fois pour tout le royaume : EmailOtpSendNode, mais aussi MagicLinkSendNode (lien magique), s’en servent.
Étape 4 : le graphe#
false), un code expiré en fait envoyer un nouveau (expired).| Étape | Nœud | Rôle |
|---|---|---|
| Identifiant et mot de passe | PageNode (UsernameCollectorNode + PasswordCollectorNode) | Un seul écran pour les deux champs |
| Vérification du mot de passe | DataStoreNode | Contrôle dans l’annuaire du royaume |
| Envoi du code par e-mail | EmailOtpSendNode | Tire un code, le garde dans l’état partagé du parcours et l’envoie à l’adresse de l’utilisateur ; error si l’utilisateur n’a pas d’adresse ou si l’envoi échoue |
| Code reçu par e-mail | EmailOtpVerifyNode | Compare le code saisi : true s’il est juste, false s’il est faux, exceeded s’il est faux et que c’était le dernier essai (maxAttempts), expired s’il a expiré |
Le nœud d’envoi, dans le graphe :
"envoi": {
"type": "EmailOtpSendNode",
"config": {
"otpLength": 6,
"ttlSeconds": 300,
"emailAttribute": "mail",
"emailSubject": "Votre code de connexion TOSIAM",
"emailBodyTemplate": "Bonjour,\n\nVotre code de connexion est : {otp}\n\nIl expire dans {ttl} secondes. Si vous n'êtes pas à l'origine de cette demande, ignorez ce message."
},
"outcomes": { "sent": "code", "error": "echec" }
},
"code": {
"type": "EmailOtpVerifyNode",
"config": { "maxAttempts": 3 },
"outcomes": { "true": "succes", "false": "code", "exceeded": "echec", "expired": "envoi" }
}Dans emailBodyTemplate, {otp} est remplacé par le code et {ttl} par sa durée de validité en secondes. Le message part en texte brut, encodé en UTF-8 : les accents passent sans précaution. L’objet (emailSubject) ne doit pas contenir de retour à la ligne, sinon l’envoi est refusé.
Le câblage de EmailOtpVerifyNode accorde trois essais :
false→EmailOtpVerifyNode: après un code faux, la saisie revient ;exceeded→ Échec : le troisième code faux termine la connexion ;expired→EmailOtpSendNode: un nouvel e-mail part et le nouveau code est demandé.
Le graphe complet est disponible en JSON : connexion-email.json. Chaque nœud est décrit dans Nœuds : OTP SMS / Email.
Étape 5 : importer et exposer le graphe#
Dans le royaume ref :
- Graphes d’authentification › Importer : choisissez
connexion-email.json. - Ouvrez le graphe et cliquez sur Exposer ce graphe comme parcours de connexion.
Le parcours est accessible à l’adresse :
http://localhost:8080/tosiam/auth/?realm=ref&module=connexion-emailÉtape 6 : se connecter#
Ouvrez l’adresse du parcours et connectez-vous avec dduck.
Le mot de passe est juste : TOSIAM envoie l’e-mail et demande le code.
Ouvrez http://localhost:8025 dans un autre onglet : le faux serveur affiche l’e-mail reçu, avec l’expéditeur, l’objet, le texte et le code en gros.
Le terminal du faux serveur l’annonce aussi :
2026-10-23T07:36:23.507Z e-mail pour donald.duck@example.com : Votre code de connexion TOSIAMSaisissez le code : la connexion aboutit. Un code faux redemande la saisie ; au troisième code faux, la connexion se termine sur la page Échec de la connexion à TOSIAM.
Pour vérifier sans navigateur, le même parcours se joue par l’API REST : voir Authentification REST, avec authIndexType=module&authIndexValue=connexion-email. Le code se saisit dans le PasswordCallback de l’étape EmailOtpVerifyNode.
En production : un vrai serveur SMTP#
Seul le service de messagerie du royaume change ; le graphe reste le même.
| Réglage | Champ REST | Valeurs |
|---|---|---|
| Nom d’hôte | hostname | Serveur SMTP de l’organisation ou du fournisseur d’envoi (smtp.example.com) |
| Port | port | 465 pour SSL, 587 pour STARTTLS, 25 pour Non SSL |
| Connexion sécurisée | sslState | SSL (TLS dès la connexion, la valeur par défaut), STARTTLS (connexion en clair puis passage en TLS ; l’envoi échoue si le serveur ne le propose pas) ou Non SSL |
| Nom d’utilisateur, mot de passe | username, password | Compte d’envoi ; l’authentification SMTP n’est faite que si les deux sont renseignés |
| Adresse d’expédition | from | Adresse que le serveur SMTP accepte comme expéditeur, avec ou sans nom affiché |
TOSIAM garde le mot de passe chiffré dans sa configuration et ne le renvoie jamais en lecture ("password": null).
Réglages utiles#
| Réglage | Nœud | Effet |
|---|---|---|
ttlSeconds (300) | EmailOtpSendNode | Durée de validité du code, en secondes |
emailSubject | EmailOtpSendNode | Objet de l’e-mail, sur une seule ligne |
emailBodyTemplate | EmailOtpSendNode | Texte de l’e-mail, avec {otp} et {ttl} |
emailAttribute (mail) | EmailOtpSendNode | Attribut du profil qui contient l’adresse |
emailSharedStateKey (email) | EmailOtpSendNode | Clé de l’état partagé lue avant le profil |
otpLength (6) | EmailOtpSendNode | Longueur du code |
maxAttempts (5) | EmailOtpVerifyNode | Nombre de codes faux admis ; le dernier passe par exceeded |
L’adresse est cherchée d’abord dans l’état partagé, sous la clé emailSharedStateKey, puis dans le profil. Un nœud placé avant l’envoi qui y range une adresse saisie permet de vérifier une adresse que le profil n’a pas encore.
En cas de problème#
Les erreurs d’envoi mènent à la sortie error du nœud, donc à Échec de la connexion dans ce graphe ; la cause est dans catalina.out, sous la ligne Failed to send OTP email to '…' for user '…'.
Symptôme dans catalina.out | Cause probable |
|---|---|
NullPointerException: Cannot invoke "String.length()" because "s" is null | Le royaume n’a pas de service de messagerie (étape 3) |
the 'Email From Address' configuration is empty | Service présent, mais sans adresse d’expédition (from) |
MailConnectException: Couldn't connect to host, port: … | Aucun serveur SMTP à cette adresse et ce port : faux serveur arrêté, mauvais port, ou localhost vu depuis un conteneur (encadré de l’étape 3) |
MessagingException: Could not connect to SMTP host aussitôt, alors que le serveur écoute | SSL choisi pour un serveur qui attend du texte en clair ou STARTTLS : réglez Connexion sécurisée selon le port |
AuthenticationFailedException | Nom d’utilisateur ou mot de passe SMTP refusé par le serveur |
Cannot resolve email for user | L’utilisateur n’a pas d’attribut mail (étape 1) |
| Rien dans les journaux, mais l’e-mail n’arrive pas | Le serveur SMTP a accepté le message : cherchez du côté de la remise (spam, SPF/DKIM, adresse du destinataire) |
Pour aller plus loin#
- Double authentification par SMS : le même parcours, avec un code envoyé par SMS.
- Double authentification TOTP : un second facteur sans envoi, avec une application d’authentification.
- Nœuds : lien magique : connexion sans mot de passe par un lien envoyé avec le même service de messagerie.
- Nœuds : OTP SMS / Email : la référence des nœuds d’envoi et de vérification.
Mis à jour le