đȘđ ImplĂ©mente un 2FA (Two Factor Authentication)
Temps de lecture : 10 minutes
đš Cette Ă©dition est relativement longue, votre boĂźte mail risque de la tronquer. Je vous conseille de cliquer sur le titre ci-dessus pour lâouvrir dans votre navigateur web. âïž
Hello les petits Biscuits !
Bienvenue sur la 39Úme édition de Ruby Biscuit.
Vous ĂȘtes maintenant 599 abonnĂ©s đ„ł
Bonne lecture !
Bonjour Ă tous ! Aujourdâhui nous allons voir comment implĂ©menter une feature de plus en plus populaire et demandĂ©e pour amĂ©liorer la sĂ©curitĂ© de nos comptes, le 2FA. Câest parti đđ»
Mise en place
Nous allons avoir besoin de devise-two-factor (Ă supposer que vous ayez devise sur votre plateforme bien entendu).
La version la plus rĂ©cente de devise-two-factor (6.1.0 Ă lâheure oĂč jâĂ©cris ces lignes) nâest pas destinĂ©e aux versions de Rails qui prĂ©cĂšdent la 7.0. Nous allons voir ici lâimplĂ©mentation avec une version de Rails plus ancienne, celle de lâapp sur laquelle je travaille actuellement, la 6.1.x. Pour cela nous allons devoir nous tourner vers la v4.x.
Nous verrons en fin dâarticle ce quâil faut mettre en place pour les versions de Rails plus rĂ©centes.
Implémentation
Déterminer sur quel modÚle mettre en place le 2FA. Nous utiliserons la table
users.Ajouter une config
DeviseĂ notre modĂšleUserLancer la migration pour ajouter les champs en ajoutant un
default: falsesur la colonneotp_required_for_loginsi vous voulez feature flaguer par utilisateur.Ajouter la durée de validité du code dans
devise.rb:Enfin plus globalement, suivre la doc Ă©tape par Ă©tape est recommandĂ©e puisque cette derniĂšre est trĂšs bien faite đ
Nous allons désormais nous occuper de notre SessionsController et mettre en place nos conditions. On veut pouvoir require le two factor sous certaines conditions donc ajoutons cette logique avec 2 callbacks :
NB : si vous nâavez pas dâenvironnement de production cette configuration peut vous sembler inutile puisque vous souhaitez lâactiver sans condition aucune.
Le require_no_authentication nous permet dâĂ©viter un flash error de devise comme quoi nous sommes dĂ©jĂ connectĂ© lors de lâauthentification avec le 2FA.
VoilĂ pour la configuration globale de la gem !
Désormais nous allons ajouter notre logique métier et envoyer un mail avec un code de confirmation lorsque le 2FA est actif.
1Úre étape :
La variable dâenvironnement 2FA_ENCRYPTION_KEY nâest pas lĂ pour faire joli. La mĂ©thode generate_otp_secret de la gem sert Ă gĂ©nĂ©rer un secret sur lâobjet visĂ©. En effet, cela va gĂ©nĂ©rer des valeurs dans les champs que nous avons ajoutĂ© dans la migration. Ă savoir entre autre dans :
otp_secret, qui est trouvable dans la documentation, est obligatoire pour faire marcher le 2FA. Si vous tentez de vous connecter avec le two factor sans que votre utilisateur ai dâotp_secret alors vous aurez une 500.
Je vous conseille donc dâajouter la ligne suivante Ă chaque crĂ©ation de User sur votre plateforme :
Si ensuite vous souhaitez feature flaguer par User vous pouvez ajouter 2 méthodes dans le modÚle et les utiliser comme bon vous semble :
2Úme étape :
Direction le SessionsController pour y ajouter du code :
Nous avons beaucoup de choses à décortiquer et nous allons regarder chaque étape de authenticate_with_two_factor pour comprendre.
Tout dâabord si notre resource est vide nous voulons render le formulaire avec un message dâerreur.
Ensuite nous allons vĂ©rifier si dans le cookie le param âremember meâ (
remember_2fa_id) est prĂ©sent. Vous savez câest la petite checkbox qui ressemble à ça habituellement đ
Auquel cas si le mdp est correct, on ne redemandera pas de passer par une deuxiÚme étape. Nous reviendrons sur cette histoire de cookie un peu plus tard.
Sinon nous allons regarder si le param
otp_attemptest prĂ©sent. Il faut savoir que pour le 2FA nous avons 3 params obligatoires, lâemail, le mdp et otp_attempt.
Nous allons Ă©galement checker si un param de session (otp_user_id) est prĂ©sent (quâon set par la suite), auquel cas nous consommons le tokenvalidate_and_consume_otp!, effaçons le param de session, enregistronsremember_2fa_iddans le cookie si lâutilisateur a cochĂ© la case et enfin lâauthentifions.
NB : la mĂ©thodevalidate_and_consume_otp!est trouvable dans la documentation.DerniĂšre Ă©tape, crĂ©ons un token de session qui nous servira pour le mail puis envoyons ce dernier. NĂ©anmoins nous y ajoutons un lock pour ne pouvoir gĂ©nĂ©rer quâun mail avec code unique par tranche horaire. Câest de la logique mĂ©tier et non obligatoire, vous pouvez vous en passer.
Si aucunes des conditions prĂ©alables nâont Ă©tĂ© rĂ©unies cela signifie que le mdp est incorrect et nous affichons un message dâerreur.
Avec cela nous avons terminĂ© la premiĂšre Ă©tape du 2FA. Ătape suivante, si je dois me connecter et rentrer un code de connexion alors jâai besoin que lâon mâaffiche un formulaire !
3Úme étape :
CrĂ©er un fichier HTML (ici nous rentrons dans âusers/sessions/two_factorâ) pour y intĂ©grer le formulaire. Libre Ă vous de choisir comment le prĂ©senter, mais cela peut ressembler Ă cela :
Une fois le mail reçu ainsi que le code quâil contient (il vous faudra afficher current_otp Ă votre utilisateur) nous allons rentrer ce code et repasser dans le SessionController puisque le formulaire est une requĂȘte POST sur ce dernier.
Tout le chemin du authenticate_with_two_factor va donc ĂȘtre rĂ©pĂ©tĂ© et vous pouvez dĂ©sormais comprendre lâintĂ©rĂȘt de remember_2fa_id. Si ce dernier Ă©tait prĂ©sent Ă la premiĂšre Ă©tape alors nous ne serions pas passĂ©s par ce formulaire et le controller nous aurait redirigĂ©.
Nous remarquons ici nĂ©anmoins le bouton âRenvoyer le codeâ et nous allons comprendre pourquoi nous avons mis en cache le otp_token :
Câest ici quâintervient notre controller dâenvoi de mail.
Pour Ă©viter dâenvoyer le mail Ă une mauvaise adresse nous allons vĂ©rifier que la valeur en cache est Ă©gale au param passĂ© au controller, si tel est le cas on ajoute un lock (pour Ă©viter le spam) et on envoie le mail, sinon on redirige.
Pour une meilleure expĂ©rience utilisateur, gĂ©rer cet envoi en AJAX est recommandĂ© đ
Tests
Pour les tests il y a beaucoup de cas Ă tester mais les plus globaux sont ceux-ci pour le SessionsController (nâhĂ©sitez pas Ă tester tous les cas) :
Et pour le SendOtpPasswordEmailsController :
En conclusion
Vous avez maintenant toutes les cartes en main pour mettre en oeuvre votre 2FA au sign in sur votre app ! Reste à décider, selon vos besoins et logiques métier, comment vous souhaitez organiser tout cela.
Pensez bien Ă tester tous les cas de figures que vos utilisateurs pourraient avoir envie dâessayer. Notre otp_user_id par exemple sert, entre autre, Ă conserver le bon id pour vĂ©rifier que si Ă lâĂ©tape du 2FA je fais un retour arriĂšre et quâensuite je rentre une autre adresse mail (car je peux avoir 2 comptes diffĂ©rents), alors le mail sâenvoie Ă la nouvelle adresse mail et non pas Ă lâancienne !
Merci pour votre lecture et jâespĂšre que lâarticle vous a plu đ
Bonus : ce quâil faut faire pour Rails 7+
Ajouter dâautres variables dâenvironnement
La migration est différente.
Retirer
database_authenticatablede votre modĂšle puisque les 2 modules sont incompatibles. Cela se fait normalement tout seul avec la version 4.x.
Mais pour plus de précisions je vous invite à regarder la documentation.
â Stanislas













