Introduction
Dans l’article précédent, nous avons mis en place la vérification des adresses email à l’aide d’un code OTP (One-Time Password) généré aléatoirement. Cet article nous a permis d’ajouter une couche supplémentaire de sécurité à notre application Django. Aujourd’hui, nous allons implémenter une nouvelle fonctionnalité : la réinitialisation du mot de passe en cas d’oubli. Dans notre approche, nous enverrons un email à l’utilisateur contenant un lien lui permettant de créer un nouveau mot de passe.
1. Création de la fonction d'envoi d'email et du générateur de token de réinitialisation de mot de passe
Dans le fichier tokens.py, ajoutez le code suivant :
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six
from users.models import User
class PasswordResetTokenGeneration(PasswordResetTokenGenerator):
def _make_hash_value(self, user: User, timestamp: int) -> str:
return ( six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.password))
password_reset_token = PasswordResetTokenGeneration()
Dans le fichier emails.py, ajoutez la fonction qui envoie l'email de réinitialisation :
def send_reset_password_link(request, user):
subject = 'Reset your password'
domain = get_current_site(request).domain
uid = urlsafe_base64_encode(force_bytes(user.pk))
token = password_reset_token.make_token(user)
link = f'http://{domain}{reverse("users:reset-password", kwargs={"uidb64": uid, "token": token})}' # noqa
body = f'Click <a href={link}>here</a> to reset your password.'
send_mail(subject, body, [user.email])
Importez les éléments nécessaire :
from django.contrib.sites.shortcuts import get_current_site
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from users.tokens import password_reset_token
2. Création des formulaires
Dans le fichier forms.py, ajoutez les formulaires suivants :
class SendResetPasswordEmailForm(Form):
email = forms.CharField(label='Email',
widget=forms.EmailInput(
attrs={'class': 'form-control', 'id': 'email', 'placeholder': 'Email'}), required=True, )
class ResetPasswordForm(Form):
password = forms.CharField(label='Password',
widget=forms.PasswordInput(
attrs={'class': 'form-control', 'id': 'password', 'placeholder': 'Password'}),
required=True, )
confirm_password = forms.CharField(label='Confirm password',
widget=forms.PasswordInput(
attrs={'class': 'form-control', 'id': 'confirm_password',
'placeholder': 'Confirm password'}),
required=True, )
3. Création des vues
Dans le fichier views.py, créez les deux vues nécessaires à la réinitialisation du mot de passe, en ce qui concerne les templates utilisés dans les vues, vous pouvez y avoir accès directement sur le dépôt github :
class SendResetPasswordEmailView(View):
def get(self, request):
form = SendResetPasswordEmailForm()
return render(self.request, template_name='registration/send_reset_password_email.html', context={"form": form})
def post(self, request):
form = SendResetPasswordEmailForm(self.request.POST)
if form.is_valid():
try:
user = User.objects.get(email=form.cleaned_data['email'])
with transaction.atomic():
try:
send_reset_password_link(request, user)
messages.success(request, "We have sent reset password mail", "success")
except smtplib.SMTPException:
messages.error(request, "Error when sending mail please try again later", "danger")
except User.DoesNotExist:
messages.error(request, "User with this email address does not exist", "danger")
return render(request, template_name='registration/send_reset_password_email.html', context={"form": form})
def reset_password(request, uidb64, token):
form = ResetPasswordForm()
context = {}
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and password_reset_token.check_token(user, token):
if request.method == 'POST':
form = ResetPasswordForm(request.POST)
if form.is_valid():
password = form.cleaned_data['password']
user.set_password(password)
user.save()
messages.success(request, "Your password has been reset.", "success")
return redirect("login")
else:
context['error'] = True
context['form'] = form
return render(request, template_name='registration/reset_password.html', context=context)
Et les importations :
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
from .tokens import password_reset_token
from .emails import send_reset_password_link
from .forms import SendResetPasswordEmailForm, ResetPasswordForm
4. Ajout des URLs dans urls.py
Dans le fichier urls.py de l'application users, ajoutez les chemins suivants :
path('reset-password/<uidb64>/<token>/', reset_password, name='reset-password'),
path('send-reset-password-link/', SendResetPasswordEmailView.as_view(), name='send-reset-password-link')
Conclusion
La fonctionnalité de réinitialisation de mot de passe est cruciale dans le processus d’authentification d’une application web. Dans cet article, nous avons vu comment la mettre en place en envoyant un lien par email à l’utilisateur. Dans le prochain article, nous aborderons la fonctionnalité de changement de mot de passe depuis le compte utilisateur. Vous pouvez consulter le code source de cet article en cliquant ici.