Série : Symfony 7
Fichiers : https://github.com/NouvelleTechno/OpenBlog
Dans ce 7ème tutoriel, nous allons implémenter une fonctionnalité permettant aux utilisateurs de réinitialiser leur mot de passe en utilisant Symfony 7. Nous utiliserons le service JSON Web Token (JWT) et le service d'envoi d'email configuré précédemment.
Configuration de la Route pour Mot de Passe Oublié
Ajouter la Route et la Méthode dans le Contrôleur
Dans le fichier src/Controller/SecurityController.php
nous allons ajouter la méthode forgottenPassword
C'est la méthode qui gérera la logique de la réinitialisation de mot de passe. Elle contiendra le code pour afficher le formulaire, traiter la soumission du formulaire, générer un token JWT et envoyer un email.
#[Route('/mot-de-passe-oublie', name: 'forgotten_password')]
public function forgottenPassword(
Request $request,
UsersRepository $usersRepository,
JWTService $jwt,
SendEmailService $mail
) : Response
{
$form = $this->createForm(ResetPasswordRequestFormType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
// Le formulaire est envoyé ET valide
// On va chercher l'utilisateur dans la base
$user = $usersRepository->findOneByEmail($form->get('email')->getData());
// On vérifie si on a un utilisateur
if($user){
// On a un utilisateur
// On génère un JWT
// Header
$header = [
'typ' => 'JWT',
'alg' => 'HS256'
];
// Payload
$payload = [
'user_id' => $user->getId()
];
// On génère le token
$token = $jwt->generate($header, $payload, $this->getParameter('app.jwtsecret'));
// On génère l'URL vers reset_password
$url = $this->generateUrl('reset_password', ['token' => $token], UrlGeneratorInterface::ABSOLUTE_URL);
// Envoyer l'e-mail
$mail->send(
'no-reply@openblog.test',
$user->getEmail(),
'Récupération de mot de passe sur le site OpenBlog',
'password_reset',
compact('user', 'url') // ['user' => $user, 'url'=>$url]
);
$this->addFlash('success', 'Email envoyé avec succès');
return $this->redirectToRoute('app_login');
}
// $user est null
$this->addFlash('danger', 'Un problème est survenu');
return $this->redirectToRoute('app_login');
}
return $this->render('security/reset_password_request.html.twig', [
'requestPassForm' => $form->createView()
]);
}
Créer le Template Twig pour le Formulaire
Créez le fichier templates/security/reset_password_request.htmltwig
, il contiendra le code HTML pour afficher le formulaire de demande de réinitialisation de mot de passe.
Ajoutez le code suivant pour afficher le formulaire de demande de réinitialisation de mot de passe.
{% extends 'base.html.twig' %}
{% block title %}Demande de réinitialisation de mot de passe{% endblock %}
{% block body %}
<section>
<h1>Demande de réinitialisation de mot de passe</h1>
{{ form_start(requestPassForm) }}
{{ form_row(requestPassForm.email) }}
<button type="submit">Envoyer</button>
{{ form_end(requestPassForm) }}
</section>
{% endblock %}
Création du Formulaire de Demande de Réinitialisation de Mot de Passe
Pour créer le formulaire de demande de réinitialisation de mot de passe, nous allons exécuter la commande suivante :
symfony console make:form ResetPasswordRequestFormType
Nous n'allons pas relier ce formulaire à une entité, il faudra donc modifier le code pour ajouter l'e-mail comme suit
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ResetPasswordRequestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}
Configuration de l'Email de Réinitialisation de Mot de Passe
Nous allons maintenant créer le contenu de l'email qui sera envoyé à l'utilisateur en créant le fichier templates/emails/password_reset.html.twig
<p>Bonjour {{ user.nickname }}</p>
<p>Pour votre demande de réinitialisation de mot de passe, veuillez cliquer sur le lien ci-dessous.</p>
<p><a href="{{ url|raw }}">{{ url|raw }}</a></p>
<p>Merci</p>
Création de la Route pour Réinitialiser le Mot de Passe
Pour pouvoir réinitialiser le mot de passe, nous avons besoin d'une route spécifique reset_password que nous allons ajouter dans le SecurityController
#[Route('/mot-de-passe-oublie/{token}', name: 'reset_password')]
public function resetPassword(
$token,
JWTService $jwt,
UsersRepository $usersRepository,
Request $request,
UserPasswordHasherInterface $passwordHasher,
EntityManagerInterface $em
): Response
{
// On vérifie si le token est valide (cohérent, pas expiré et signature correcte)
if($jwt->isValid($token) && !$jwt->isExpired($token) && $jwt->check($token, $this->getParameter('app.jwtsecret'))){
// Le token est valide
// On récupère les données (payload)
$payload = $jwt->getPayload($token);
// On récupère le user
$user = $usersRepository->find($payload['user_id']);
if($user){
$form = $this->createForm(ResetPasswordFormType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$user->setPassword(
$passwordHasher->hashPassword($user, $form->get('password')->getData())
);
$em->flush();
$this->addFlash('success', 'Mot de passe changé avec succès');
return $this->redirectToRoute('app_login');
}
return $this->render('security/reset_password.html.twig', [
'passForm' => $form->createView()
]);
}
}
$this->addFlash('danger', 'Le token est invalide ou a expiré');
return $this->redirectToRoute('app_login');
}
Nous allons ensuite ajouter le fichier templates/security/reset_password.html.twig pour afficher le formulaire de demande du nouveau mot de passe.
{% extends 'base.html.twig' %}
{% block title %}Demande de réinitialisation de mot de passe{% endblock %}
{% block body %}
<section>
<h1>Demande de réinitialisation de mot de passe</h1>
{{ form_start(passForm) }}
{{ form_row(passForm.password) }}
<button type="submit">Envoyer</button>
{{ form_end(passForm) }}
</section>
{% endblock %}
Création du Formulaire de Réinitialisation de Mot de Passe
Nous devons créer le formulaire à afficher dans le template créé précédemment au moyen de la commande
symfony console make:form ResetPasswordFormType
Nous modifions ensuite le code pour demander le mot de passe
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ResetPasswordFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('password', PasswordType::class, [
'label' => 'Entrez votre mot de passe'
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}
Conclusion
Vous avez maintenant implémenté une fonctionnalité complète de réinitialisation de mot de passe dans Symfony 7. Cette fonctionnalité comprend :
- Une route pour demander la réinitialisation de mot de passe.
- Un formulaire pour entrer l'adresse email.
- L'envoi d'un email avec un lien de réinitialisation contenant un token JWT.
- Une route pour réinitialiser le mot de passe avec un formulaire pour entrer le nouveau mot de passe.
- La validation du token JWT et la mise à jour du mot de passe dans la base de données.
N'hésitez pas à étendre cette fonctionnalité pour inclure des validations supplémentaires et des messages d'erreur plus détaillés.
Obtenir de l'aide
Pour obtenir de l'aide, vous pouvez accéder au serveur Discord pour une entraide par chat.