Inviter un membre par code : hash, TTL, rate-limit
Comment on a construit un flow d'invitation et de connexion par code avec hash, expiration, verrouillage et garde-fous IP.
- Auth
- Engineering
Envoyer un code paraît anodin. C’est justement pour ça que ce genre de flow est souvent traité trop vite, comme si le sujet s’arrêtait à “générer 6 caractères puis comparer”.
Le vrai sujet est ailleurs : un code d’invitation est déjà un secret d’accès. Court, temporaire, mais un secret quand même.
Le problème à résoudre
Dans un produit ou l’on veut inviter de nouveaux membres et reconnecter des membres existants, un flow par code doit tenir ensemble plusieurs contraintes :
- rester simple pour l’utilisateur
- fonctionner sur plusieurs canaux d’envoi
- expirer proprement
- limiter les abus
- rester defendable quand on relit le systeme six mois plus tard
Le piège classique, c’est de traiter ce code comme un détail de produit. On envoie un code, on le stocke, on le compare, et on passe à autre chose.
Le problème, c’est que dès que ce code permet de se connecter ou d’activer un accès, il faut le raisonner comme un mot de passe temporaire.
La solution retenue
La solution retenue est volontairement simple :
- on stocke un dérivé du code, pas le code en clair
- on limite sa durée de vie
- on compte les échecs
- on ajoute un garde-fou côté IP
Dit autrement, on ne cherche pas une astuce “passwordless”. On cherche un flow lisible, fermé et rejouable sans surprise.
Le parcours reste clair :
- émission d’un code court
- envoi au membre
- vérification du code
- refus si le code est expiré, trop essayé ou utilisé dans une fenêtre suspecte
Le point important n’est pas la complexité du flow. C’est le fait de ne pas oublier la moitié des contraintes autour.
Pourquoi cette solution tient bien
Ce type de solution tient bien quand les règles de sécurité restent visibles.
Les bons garde-fous sont connus d’avance :
- durée de validité du code
- nombre maximal de tentatives
- fenêtre de limitation côté IP
- distinction claire entre émission, vérification et refus
Quand ces règles sont explicites, elles deviennent :
- faciles à relire
- faciles à ajuster
- cohérentes d’un canal à l’autre
- plus simples à expliquer à l’équipe
Un flow de connexion par code vieillit mal quand ses seuils sont cachés dans plusieurs coins. Il vieillit mieux quand sa politique tient en quelques règles nommées.
Comment on l’implémente
L’implémentation intéressante n’est pas “comment comparer une string”. L’implémentation intéressante, c’est l’ordre des vérifications.
On retrouve d’abord le bon contexte, puis on vérifie :
- que le code est encore valide
- qu’il n’a pas dépassé son nombre d’essais
- que la tentative courante reste acceptable
- et enfin que le secret soumis correspond bien a ce qui est attendu
Ce qui compte ici, c’est moins le détail du code que la responsabilité du système :
- ne jamais rendre le secret relisible
- rendre l’expiration explicite
- rendre les échecs traçables
- refuser proprement quand le seuil est dépassé
Le vrai pivot
Le moment important dans ce genre de sujet n’est pas l’ajout du canal SMS ou email. Le moment important, c’est celui où on arrête de traiter le code comme une donnée “temporaire donc pas grave”.
Beaucoup de flows démarrent avec un raccourci très banal : on garde trop d’information, parce que c’est pratique au début.
Puis le flow grossit :
- plus d’usages
- plus de membres
- plus de cas de support
- plus de valeur portée par ce petit code
Et ce qui était un confort de démarrage devient un vrai sujet de sécurité.
Le bon pivot consiste donc à faire tôt ce que l’on repousse souvent :
- hacher le code
- borner sa vie
- compter les échecs
- ralentir l’abus
Le garde-fou qu’on oublie le plus souvent
Le hash seul ne suffit pas.
Si on s’arrête à “on ne stocke plus le code en clair”, on protège une partie du problème, mais pas tout le flow.
Il faut aussi penser à :
- l’expiration
- le verrouillage après plusieurs erreurs
- la limitation par IP
- le refus explicite, pas implicite
La robustesse ne vient pas d’une seule bonne pratique. Elle vient de leur assemblage.
Ce que la solution retire de la base
Le gain est à la fois sécuritaire et structurel.
Ce pattern retire :
- les comparaisons directes de secrets
- les exceptions informelles “juste pour le debug”
- les seuils cachés dans plusieurs services
- les comportements différents selon le canal d’entrée
Il donne aussi une meilleure règle mentale à toute l’équipe : un code d’invitation n’est pas un petit détail UX. C’est une clé d’accès courte et temporaire.
Ce que je veux montrer à d’autres devs
Je ne veux pas montrer un flow par code “moderne”. Je veux montrer un critère de qualité.
Un flow d’invitation par code tient bien quand :
- le secret n’est pas relisible
- l’expiration est explicite
- les échecs sont comptés
- l’abus est borné
- le système refuse clairement au bon moment
Un code d’invitation reste un secret court. Le traiter comme un mot de passe temporaire est souvent la meilleure analogie.