Live Coding : Activation de compte et récupération de mot de passe avec Symfony 4

Lors de l'inscription des utilisateurs, il arrive de vouloir envoyer un code d'activation par e-mail pour s'assurer que son adresse est valide.



En complément, il peut également être utile de permettre aux inscrits de réinitialiser leur mot de passe en cas d'oubli.



Attention : cet article traite spécifiquement de la procédure de réinitialisation de mot de passe, il conviendrait d'ajouter des sécurités complémentaires comme une question de sécurité, par exemple.



Activation du compte de l'utilisateur



Il y a plusieurs façons de procéder à l'activation du compte par l'utilisateur. Nous allons utiliser un token généré aléatoirement.



Si le token existe, le compte ne sera pas activé. Un lien contenant ce token sera envoyé à l'utilisateur.



Création du token



Pour commencer, nous allons modifier notre entité "Users" pour y ajouter la propriété "activation_token". Sa valeur par défaut sera définie également.



Nous allons utiliser la commande ci-dessous pour ajouter le champ de type "string" et pouvant être vide




php bin/console make:entity


Nous effectuons ensuite la migration pour mettre à jour la base de données.




/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $activation_token;


La propriété est créée, nous allons devoir définir sa valeur par défaut lors de l'enregistrement.



Nous allons modifier le fichier "RegistrationController.php" en ajoutant la ligne ci-dessous




// On génère un token et on l'enregistre
$user->setActivationToken(md5(uniqid()));


La méthode "register" contiendra donc ce code




/**
* @Route("/register", name="app_register")
*/
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder, GuardAuthenticatorHandler $guardHandler, UsersAuthenticator $authenticator): Response
{
$user = new Users();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
// encode the plain password
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$form->get('plainPassword')->getData()
)
);
// On génère un token et on l'enregistre
$user->setActivationToken(md5(uniqid()));

$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();

// do anything else you need here, like send an email

return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}

return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}


A partir de cet instant, nous allons avoir un token automatiquement généré à chaque inscription.



Nous allons ajouter un envoi d'e-mail pour que l'utilisateur puisse activer son compte.



Création de la méthode d'activation



Nous allons ajouter une méthode dans le fichier "RegistrationController.php". Cette méthode pointera vers une route du type "/activation/{token}"



Le lien dans l'e-mail devra diriger vers cette route qui vérifiera si le token existe et ensuite valider le compte correspondant.



Cette méthode s'écrira comme suit




/**
* @Route("/activation/{token}", name="activation")
*/
public function activation($token, UsersRepository $users)
{
// On recherche si un utilisateur avec ce token existe dans la base de données
$user = $users->findOneBy(['activation_token' => $token]);

// Si aucun utilisateur n'est associé à ce token
if(!$user){
// On renvoie une erreur 404
throw $this->createNotFoundException('Cet utilisateur n\'existe pas');
}

// On supprime le token
$user->setActivationToken(null);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();

// On génère un message
$this->addFlash('message', 'Utilisateur activé avec succès');

// On retourne à l'accueil
return $this->redirectToRoute('accueil');
}


Envoi de l'e-mail à l'utilisateur



Notre route étant créée, nous allons préparer l'e-mail à envoyer à l'utilisateur.



Celui-ci devra contenir le lien d'activation.



Nous devrons créer le fichier twig et l'envoi depuis le fichier "RegistrationController.php" de la même manière que dans le tutoriel "Envoyer des e-mails avec Symfony 4"



Le fichier twig, que nous stockerons dans "/templates/emails" s'appellera "activation.html.twig" et contiendra le code suivant




<h1>Activation de votre compte</p>
<p>Vous avez créé un compte sur notre site, veuillez cliquer sur le lien ci-dessous pour l'activer</p>
<p><a href="{{ absolute_url(path('activation', {'token': token})) }}">Activer mon compte</a></p>


Pour l'envoi du mail, nous allons modifier les paramètres de la méthode "register" de notre contrôleur comme ceci




public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder, GuardAuthenticatorHandler $guardHandler, UsersAuthenticator $authenticator,\Swift_Mailer $mailer): Response


Nous allons également ajouter les lignes ci-dessous




// do anything else you need here, like send an email
// On crée le message
$message = (new \Swift_Message('Nouveau compte'))
// On attribue l'expéditeur
->setFrom('votre@adresse.fr')
// On attribue le destinataire
->setTo($user->getEmail())
// On crée le texte avec la vue
->setBody(
$this->renderView(
'emails/activation.html.twig', ['token' => $user->getActivationToken()]
),
'text/html'
)
;
$mailer->send($message);


Et voilà, l'activation de compte fonctionne.



Réinitialisation du mot de passe



Lorsque les utilisateurs oublient leur mot de passe, il peut etre utile de donner la possibilité de le réinitialiser.



Le mot de passe étant chiffré en base de donnée, il nous est impossible de le renvoyer, ce serait une faille de sécurité. Nous allons donc proposer à l'utilisateur de saisir son nouveau mot de passe après l'avoir authentifié en lui envoyant un lien par e-mail.



Comme pour l'activation, nous utiliserons un token d'authentification. Il est possible d'utiliser le même, nous allons en créer un nouveau, en utilisant une méthode différente.



Création du token



Pour commencer, nous allons modifier notre entité "Users" pour y ajouter la propriété "reset_token". Sa valeur par défaut sera vide.



Nous allons utiliser la commande ci-dessous pour ajouter le champ de type "string" et pouvant être vide




php bin/console make:entity


Nous effectuons ensuite la migration pour mettre à jour la base de données.




/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $reset_token;


La propriété est créée, nous allons pouvoir l'utiliser.



Création des pages



Pour la réinitialisation du mot de passe, nous aurons besoin de deux pages :




  • Une première page permettra d'entrer l'adresse e-mail

  • La deuxième page contiendra deux champs mot de passe pour la réinitialisation



Demande de l'e-mail



Nous allons créer une route "/oubli-pass" qui affichera un champ demandant l'adresse e-mail de l'utilisateur.



Pour ce faire, nous allons créer une méthode dans le fichier "SecurityController.php". Cette méthode fera plusieurs choses :




  • Créer le formulaire qui demande l'adresse e-mail

  • Le transmettre à la vue

  • Le traiter



Commençons par créer le formulaire au moyen de la commande




php bin/console make:form ResetPassType


Ce formulaire ne sera pas lié à une entité. Le fichier contiendra ce code




<?php

namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ResetPassType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('envoyer', SubmitType::class)
;
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}


Le contrôleur, qui va gérer le formulaire, contiendra le code suivant




/**
* @Route("/oubli-pass", name="app_forgotten_password")
*/
public function oubliPass(Request $request, UsersRepository $users, \Swift_Mailer $mailer, TokenGeneratorInterface $tokenGenerator
): Response
{
// On initialise le formulaire
$form = $this->createForm(ResetPassType::class);

// On traite le formulaire
$form->handleRequest($request);

// Si le formulaire est valide
if ($form->isSubmitted() && $form->isValid()) {
// On récupère les données
$donnees = $form->getData();

// On cherche un utilisateur ayant cet e-mail
$user = $users->findOneByEmail($donnees['email']);

// Si l'utilisateur n'existe pas
if ($user === null) {
// On envoie une alerte disant que l'adresse e-mail est inconnue
$this->addFlash('danger', 'Cette adresse e-mail est inconnue');

// On retourne sur la page de connexion
return $this->redirectToRoute('app_login');
}

// On génère un token
$token = $tokenGenerator->generateToken();

// On essaie d'écrire le token en base de données
try{
$user->setResetToken($token);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
} catch (\Exception $e) {
$this->addFlash('warning', $e->getMessage());
return $this->redirectToRoute('app_login');
}

// On génère l'URL de réinitialisation de mot de passe
$url = $this->generateUrl('app_reset_password', array('token' => $token), UrlGeneratorInterface::ABSOLUTE_URL);

// On génère l'e-mail
$message = (new \Swift_Message('Mot de passe oublié'))
->setFrom('votre@adresse.fr')
->setTo($user->getEmail())
->setBody(
"Bonjour,<br><br>Une demande de réinitialisation de mot de passe a été effectuée pour le site Nouvelle-Techno.fr. Veuillez cliquer sur le lien suivant : " . $url,
'text/html'
)
;

// On envoie l'e-mail
$mailer->send($message);

// On crée le message flash de confirmation
$this->addFlash('message', 'E-mail de réinitialisation du mot de passe envoyé !');

// On redirige vers la page de login
return $this->redirectToRoute('app_login');
}

// On envoie le formulaire à la vue
return $this->render('security/forgotten_password.html.twig',['emailForm' => $form->createView()]);
}


ATTENTION : à ce stade du tutoriel, vous aurez une erreur indiquant que la route "app_reset_password" n'existe pas, c'est tout à fait normal.



Il reste maintenant le fichier twig correspondant à la page de demande de l'adresse e-mail.



Le fichier "/templates/security/forgotten_password.html.twig" contiendra




{% extends 'base.html.twig' %}

{% block title %}Mot de passe oublié{% endblock %}

{% block body %}
<h1>Mot de passe oublié</h1>
{{ form(emailForm) }}
{% endblock %}


Réinitialiser le mot de passe



Une fois le lien envoyé, nous devons traiter le retour en créant le contrôleur pour la route "app_reset_password". Cette route effectuera les actions suivantes :




  • Vérifier si le token de l'URL correspond à un utilisateur

  • Afficher le formulaire permettant de saisir le mot de passe



On commence par le contrôleur




/**
* @Route("/reset_pass/{token}", name="app_reset_password")
*/
public function resetPassword(Request $request, string $token, UserPasswordEncoderInterface $passwordEncoder)
{
// On cherche un utilisateur avec le token donné
$user = $this->getDoctrine()->getRepository(Users::class)->findOneBy(['reset_token' => $token]);

// Si l'utilisateur n'existe pas
if ($user === null) {
// On affiche une erreur
$this->addFlash('danger', 'Token Inconnu');
return $this->redirectToRoute('app_login');
}

// Si le formulaire est envoyé en méthode post
if ($request->isMethod('POST')) {
// On supprime le token
$user->setResetToken(null);

// On chiffre le mot de passe
$user->setPassword($passwordEncoder->encodePassword($user, $request->request->get('password')));

// On stocke
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();

// On crée le message flash
$this->addFlash('message', 'Mot de passe mis à jour');

// On redirige vers la page de connexion
return $this->redirectToRoute('app_login');
}else {
// Si on n'a pas reçu les données, on affiche le formulaire
return $this->render('security/reset_password.html.twig', ['token' => $token]);
}

}


Le fichier twig correspondant à cette page s'appellera "reset_password.html.twig" et contiendra le code suivant




{% extends 'base.html.twig' %}

{% block title %}Nouveau mot de passe{% endblock %}

{% block body %}
<h1>Réinitialisation du mot de passe</h1>
<p>Veuillez entrer votre nouveau mot de passe ci-dessous.</p>
<form method="post">
<label for="inputPassword">Password</label>
<input type="password" name="password" id="inputPassword" placeholder="Mot de passe" required>
<input type="hidden" name="token" value="{{ token }}">
<button type="submit" id="valider">
Valider le mot de passe
</button>
</form>
{% endblock %}


Obtenir de l'aide



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

Live Coding : Activation de compte et récupération de mot de passe avec Symfony 4
Article publié le - Modifié le

Catégories : MVC Tutoriel Symfony Live-Coding

Mots-clés : Tutoriel MVC Utilisateurs Symfony Live-Coding compte activation oublipass

Partager : Partager sur Facebook Partager sur Twitter Partager sur LinkedIn