11 - Validation frontend - Entropie de mots de passe (Symfony 7)

Temps de lecture : 15 minutes environ.

Dans ce tutoriel, nous allons nous concentrer sur la mise en place de validations de formulaire côté frontend pour notre projet Symfony, plus spécifiquement pour le formulaire d'inscription de notre application OpenBlog. Cette approche complète les validations backend que nous avons mises en place dans le tutoriel précédent.

Pourquoi la Validation Frontend ?

La validation frontend est essentielle pour améliorer l'expérience utilisateur en fournissant des retours immédiats sur les erreurs de saisie, sans avoir besoin de soumettre le formulaire. Cependant, il est crucial de se rappeler que cette validation peut être contournée, d'où l'importance de la validation backend.

Mise en Place des Validations sur le Formulaire d'Inscription

Nous allons commencer par ajouter un indicateur de complexité pour le mot de passe, ainsi que des vérifications pour le pseudo et l'email.

Ajout de l'Indicateur de Complexité du Mot de Passe

Tout d'abord, nous allons modifier le template register.html.twig pour ajouter un indicateur de complexité du mot de passe. Voici comment faire :

<div>
<label for="password">Mot de passe</label>
{{ form_widget(form.plainPassword) }}
<p>Complexité du mot de passe : <span id="entropie" class="text-red">Très faible</span></p>
</div>

Ensuite, nous allons styliser les niveaux de complexité avec du CSS :

.text-red { color: red; }
.text-orange { color: orange; }
.text-green { color: green; }

Ces styles seront utilisés pour indiquer visuellement la complexité du mot de passe.

Création du Script JavaScript pour la Validation

Nous allons maintenant créer un fichier JavaScript pour gérer la validation en temps réel du formulaire.

Créez un fichier register.js dans le dossier assets/js/ et ajoutez le code suivant :

// Variables booléennes
let pseudo = false;
let email = false;
let rgpd = false;
let pass = false;

// On charge les éléments du formulaire
document.querySelector("#registration_form_nickname").addEventListener("input", checkPseudo);
document.querySelector("#registration_form_email").addEventListener("input", checkEmail);
document.querySelector("#registration_form_agreeTerms").addEventListener("input", checkRgpd);
document.querySelector("#registration_form_plainPassword").addEventListener("input", checkPass);

function checkPseudo(){
pseudo = this.value.length > 2;
checkAll();
}

function checkEmail(){
let regex = new RegExp("\\S+@\\S+\\.\\S+");
email = regex.test(this.value);
checkAll();
}

function checkRgpd(){
rgpd = this.checked;
checkAll();
}

function checkAll(){
document.querySelector("#submit-button").setAttribute("disabled", "disabled");
if(email && pseudo && pass && rgpd){
document.querySelector("#submit-button").removeAttribute("disabled");
}
}

const PasswordStrength = {
STRENGTH_VERY_WEAK: 'Très faible',
STRENGTH_WEAK: 'Faible',
STRENGTH_MEDIUM: 'Moyen',
STRENGTH_STRONG: 'Fort',
STRENGTH_VERY_STRONG: 'Très fort',
}

function checkPass(){
// On récupère le mot de passe tapé
let mdp = this.value;

// On récupère l'élément d'affichage de l'entropie
let entropyElement = document.querySelector("#entropy");

// On évalue la force du mot de passe
let entropy = evaluatePasswordStrength(mdp);

entropyElement.classList.remove("text-red", "text-orange", "text-green");

// On attribue la couleur en fonction de l'entropie
switch(entropy){
case 'Très faible':
entropyElement.classList.add("text-red");
pass = false;
break;
case 'Faible':
entropyElement.classList.add("text-red");
pass = false;
break;
case 'Moyen':
entropyElement.classList.add("text-orange");
pass = false;
break;
case 'Fort':
entropyElement.classList.add("text-green");
pass = true;
break;
case 'Très fort':
entropyElement.classList.add("text-green");
pass = true;
break;
default:
entropyElement.classList.add("text-red");
pass = false;
}

entropyElement.textContent = entropy;

checkAll();
}

function evaluatePasswordStrength(password){
// On calcule la longueur du mot de passe
let length = password.length;

// Si le mot de passe est vide
if(!length){
return PasswordStrength.STRENGTH_VERY_WEAK;
}

// On crée un objet qui contiendra les caractères et leur nombre
let passwordChars = {};

for(let index = 0; index < password.length; index++){
let charCode = password.charCodeAt(index);
passwordChars[charCode] = (passwordChars[charCode] || 0) + 1;
}

// Compte le nombre de caractères différents dans le mot de passe
let chars = Object.keys(passwordChars).length;

// On initialise les variables des types de caractères
let control = 0, digit = 0, upper = 0, lower = 0, symbol = 0, other = 0;

for(let [chr, count] of Object.entries(passwordChars)){
chr = Number(chr);
if(chr < 32 || chr === 127){
// Caractère de contrôle
control = 33;
}else if(chr >= 48 && chr <= 57){
// Chiffres
digit = 10;
}else if(chr >= 65 && chr <= 90){
// Majuscules
upper = 26;
}else if(chr >= 97 && chr <= 122){
// Minuscules
lower = 26;
}else if(chr >= 128){
// Autres caractères
other = 128;
}else{
// Symboles
symbol = 33;
}
}

// On calcule le pool de caractères
let pool = control + digit + upper + lower + other + symbol;

// Formule de calcul de l'entropie
let entropy = chars * Math.log2(pool) + (length - chars) * Math.log2(chars);

if(entropy >= 120){
return PasswordStrength.STRENGTH_VERY_STRONG;
}else if(entropy >= 100){
return PasswordStrength.STRENGTH_STRONG;
}else if(entropy >= 80){
return PasswordStrength.STRENGTH_MEDIUM;
}else if(entropy >= 60){
return PasswordStrength.STRENGTH_WEAK;
}else{
return PasswordStrength.STRENGTH_VERY_WEAK;
}
}

Ce script ajoute des écouteurs d'événements sur les champs du formulaire pour valider les entrées en temps réel. Il calcule la complexité du mot de passe et met à jour l'interface en conséquence.

Intégration du JavaScript dans Symfony

Enfin, nous devons inclure ce script dans notre page de formulaire. Pour cela, modifiez importmap.php pour ajouter notre script register.js :

    'register' => [
'path' => './assets/js/register.js',
'entrypoint' => true,
],

Puis, dans votre template register.html.twig, ajoutez le bloc importmap pour charger ce script :

{% block importmap %}
{{ parent() }}
{{ importmap('register') }}
{% endblock %}

Conclusion

En combinant les validations frontend et backend, nous assurons que notre formulaire est à la fois sécurisé et convivial. La validation frontend fournit des retours immédiats à l'utilisateur, tandis que la validation backend garantit l'intégrité des données.

Obtenir de l'aide

Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat.

11 - Validation frontend - Entropie de mots de passe (Symfony 7)
Article publié le

Catégorie : Symfony

Mots-clés : Symfony 7

Partager : Partager sur Facebook Partager sur Twitter Partager sur LinkedIn