Training Menu

Mise en place d’une vérification d’adresse e-mail

Kevin TIAKOUANG DJOU
Kevin TIAKOUANG DJOU
Oct. 26, 2024 · 47.31 min read
0
Django
Mise en place d’une vérification d’adresse e-mail

 

Introduction

Dans l’article précédent, nous avons mis en place une authentification via l’adresse email, une méthode courante sur les plateformes modernes. Cependant, cette méthode nécessite une vérification de l'email pour s'assurer que celui-ci appartient bien à l’utilisateur. Dans cet article, nous allons voir comment réaliser cette vérification à l’aide d’un code OTP (One-Time Password) généré aléatoirement.

1. Création du modèle OTP

Dans l’application users, créez un modèle Otp pour représenter les OTP. Ce modèle contiendra l'utilisateur associé, le code OTP, et une date d'expiration.

class Otp(models.Model):

    user = models.ForeignKey(

        "User",

        on_delete=models.CASCADE,

    )

    code = models.CharField(max_length=6)

    expiration_at = models.DateTimeField()

    created_at = models.DateTimeField(auto_now_add=True)

    updated_at = models.DateTimeField(auto_now=True)

 

    def __str__(self):

        return f"{self.user.email}'s OTP"

 

Puis on réalise une modification sur le modèle User en ajoutant un nouveau champ et on réalise les migrations :

email_verified_at = models.DateTimeField(blank=True, null=True)

2. Configuration des paramètres dans settings.py

Ajoutez les paramètres suivants pour définir le nombre de chiffres de l’OTP, sa durée de validité, et les paramètres d'email :

# OTP constant

OTP_CODE_NUMBER_OF_DIGITS = 7

OTP_VALID_MINUTES = 10

 

# Email settings

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

EMAIL_HOST = "smtp.gmail.com"

EMAIL_FROM = "*******************@gmail.com"

EMAIL_PORT = 587

EMAIL_HOST_USER = "*******************@gmail.com"

EMAIL_HOST_PASSWORD = "*******************"

EMAIL_USE_TLS = True

 

3. Mise en œuvre de l'envoi d’email de vérification

Créez un fichier email.py dans l’application users pour gérer l'envoi des emails de vérification. Ce fichier contiendra les fonctions pour envoyer le code OTP à l’utilisateur par email.

from django.conf import settings

from django.core.mail import EmailMessage

 

 

def send_mail(subject, body, to):

    email = EmailMessage(

        subject=subject, body=body, from_email=settings.EMAIL_FROM, to=to

    )

    email.send()

 

 

def send_opt(otp):

    subject = "Vérification OTP"

 

    body = f"Your is {otp.code}\n It's valid {settings.OTP_VALID_MINUTES}"

 

    to_email = otp.user.email

 

    send_mail(subject, body, [to_email])

 

4. Mise en place du service de vérification OTP

En tout premier lieux, on cree un fichier exceptions.py dans l’application users et on y ajoute la classe suivante :

class OtpVerifyError(Exception):

    pass

 

Puis dans le fichier services.py, ajoutez le service qui générera les OTP et vérifiera les codes saisis par les utilisateurs. Ce service gérera également l'activation du compte en cas de vérification réussie.

import datetime

import random

 

from django.conf import settings

from django.utils import timezone

 

from users.exceptions import OtpVerifyError

from users.models import User, Otp

 

 

class OtpService:

    def __init__(self):

        self.number_of_digits = settings.OTP_CODE_NUMBER_OF_DIGITS

 

    def create(self, user: User) -> Otp:

        digits = self.__get_digits()

        otp = Otp.objects.create(

            code=str(random.randrange(0, digits)).zfill(self.number_of_digits),

            expiration_at=timezone.now()

                          + datetime.timedelta(minutes=settings.OTP_VALID_MINUTES),

            user_id=user.id,

        )

        return otp

 

    def __get_digits(self) -> int:

        digits = ""

        for num in range(self.number_of_digits):

            digits += "9"

        return int(digits)

 

 

class OtpVerifyService:

    def done(self, user: User, send_code: str):

        if not user.otp_set.all():

            raise OtpVerifyError("Invalid otp verify")

        otp = user.otp_set.all().last()

        if send_code != otp.code:

            raise OtpVerifyError("Wrong otp code")

        if timezone.now() > otp.expiration_at:

            raise OtpVerifyError("Expiration of validity")

        self.verify_done(user)

 

    def verify_done(self, user: User) -> None:

        user.email_verified_at = timezone.now()

        user.last_login = timezone.now()

        user.save()

 

5. Création de la vue de vérification et de renvoie du code

Implémentez une vue VerifyEmailView pour afficher un formulaire de saisie de l’OTP. Cette vue permet à l'utilisateur d'entrer le code reçu par email et de valider son adresse email.

class VerifyEmailView(View):

    template_name = 'registration/verification.html'

 

    def get(self, request, user_id):

        user = get_object_or_404(User, id=user_id)

        form = VerificationEmailForm()

        return render(self.request, template_name=self.template_name, context={"form": form, "user_id": user.id})

 

    def post(self, request, user_id):

        user = get_object_or_404(User, id=user_id)

        form = VerificationEmailForm(self.request.POST)

        if form.is_valid():

            try:

                otp = form.cleaned_data["otp"]

                OtpVerifyService().done(user, otp)

                messages.success(request, "Verification successful", "success")

                return redirect("login")

            except OtpVerifyError as e:

                messages.success(request, e, "success")

        return render(self.request, template_name=self.template_name, context={"form": form, "user_id": user.id})

 

 

def resend_verification_email(request, user_id):

    user = get_object_or_404(User, id=user_id)

    with transaction.atomic():

        otp = OtpService().create(user)

        messages.success(request, "We have resend a verification mail", "success")

        try:

            send_opt(otp)

        except smtplib.SMTPException:

            user.delete()

            messages.error(request, "Error when sending mail please try again later", "danger")

        return redirect(reverse("users:verify-email", kwargs={"user_id": user.id}))

 

Puis on ajoute les URLs correspondant dans le fichier urls.py de l’application users :

urlpatterns = [

    path('signup/', SignUpView.as_view(), name='signup'),

    path('users/logout/', CustomLogoutView.as_view(), name='logout'),

 

    path('verify-email/', VerifyEmailView.as_view(), name='verify-email'),

    path('verify-email/<int:user_id>/', VerifyEmailView.as_view(), name='verify-email'),

    path('resend-verification-email/<int:user_id>/', resend_verification_email, name='resend-verification-email'),

]

 

 

6. Modification de la vue d’inscription pour la redirection vers la vérification

Modifiez la vue d’inscription existante pour rediriger l’utilisateur vers la page de vérification après l’inscription. L’utilisateur sera marqué comme inactif tant que son email n’aura pas été vérifié.

class SignUpView(CreateView):

    form_class = CustomUserCreationForm

    template_name = 'registration/signup.html'

 

    def get_success_url(self):

        return reverse('users:verify-email', kwargs={'user_id': self.object.id})

 

    def form_valid(self, form):

        response = super().form_valid(form)

        user = form.instance

        with transaction.atomic():

            otp = OtpService().create(user)

            messages.success(self.request, "We have send a verification mail", "success")

            try:

                send_opt(otp)

            except smtplib.SMTPException:

                user.delete()

                messages.error(self.request, "Error when sending mail please try again later", "danger")

        return response

 

7. Extension : Redirection des utilisateurs non vérifiés via un Middleware

Pour améliorer l’expérience utilisateur, nous allons configurer un middleware qui redirige automatiquement les utilisateurs vers la page de vérification s'ils tentent de se connecter avec un compte non vérifié. Ce middleware intercepte les tentatives de connexion, vérifie l’état d’activation de l’utilisateur et redirige l’utilisateur vers la page de vérification si son compte est inactif.

  1. Création du Middleware de Redirection

Dans le fichier users/middlewares.py, créez le middleware InactiveUserRedirectMiddleware. Lorsqu’un utilisateur tente de se connecter avec un compte inactif, le middleware intercepte la requête et redirige vers la page de vérification avec un message d’avertissement.

from django.contrib import messages

from django.contrib.auth import authenticate

from django.shortcuts import redirect

from django.urls import reverse

 

 

class InactiveUserRedirectMiddleware:

    def __init__(self, get_response):

        self.get_response = get_response

 

    def __call__(self, request):

        if request.path == reverse('login') and request.method == 'POST':

            username = request.POST.get('username')

            password = request.POST.get('password')

            user = authenticate(request, username=username, password=password)

 

            if user is not None and not user.email_verified_at:

                messages.warning(request, 'Account not verified, verify your email box or send another verification '

                                          'email.',

                                 extra_tags='warning')

                return redirect(reverse('users:verify-email', kwargs={"user_id": user.id}))

 

        return self.get_response(request)

 

 

  1. Paramétrage du Middleware dans les Paramètres

Ajoutez InactiveUserRedirectMiddleware à la liste des middlewares dans settings.py pour qu'il soit actif dans l’application.

MIDDLEWARE = [

    'django.middleware.security.SecurityMiddleware',

    'django.contrib.sessions.middleware.SessionMiddleware',

    'django.middleware.common.CommonMiddleware',

    'django.middleware.csrf.CsrfViewMiddleware',

    'django.contrib.auth.middleware.AuthenticationMiddleware',

    'django.contrib.messages.middleware.MessageMiddleware',

    'django.middleware.clickjacking.XFrameOptionsMiddleware',

 

    'users.middlewares.InactiveUserRedirectMiddleware',

]

 

 

8. Tests visuels

Présentez des captures d’écran pour illustrer chaque étape du processus :

  • Page d’accueil

  • Inscription de l’utilisateur

  • Email de vérification re

 

  • Page de vérification avec saisie de l’OTP

  • Redirection vers la page de connexion après la vérification

  • Connexion réussie après vérification

 

Conclusion

En implémentant ce système de vérification par OTP, nous avons non seulement sécurisé le processus d'inscription, mais nous avons également ajouté un contrôle automatique de la vérification de l'e-mail lors de la connexion. Ce mécanisme garantit que seuls les utilisateurs ayant une adresse e-mail confirmée peuvent accéder à leur compte. Cette approche renforce ainsi la sécurité et améliore l’expérience utilisateur en simplifiant le processus de validation d’e-mail. Ainsi s’achève cet article, prochainement nous verrons comment gérer les mots de passe oublier et le changement de mot de passe. Vous pouvez à tout moment voire le code de cet article en cliquant ici.

 

0

Applaudissez pour montrer votre soutien

Kevin TIAKOUANG DJOU

Kevin TIAKOUANG DJOU

2 Followers · Writer for Django

Passionné de Django pour sa facilité à créer des applications web robustes, je serais ravi de mettre mes compétences en Python-Django au service to… Read more