How can I implement email verification in Django

Completely stumped! I'm using the console as my email backend. I end up with False in token_generator.check_token as a result "Invalid or expired token." is displayed in my homepage when I navigate to say "http://localhost:8000/user/verify-email/?token=cgegv3-ec1fe9eb2cebc34e240791d72fb10d7d&email=test16@example.com"

Here's my code

from django.contrib.auth.tokens import PasswordResetTokenGenerator

class CustomPasswordResetTokenGenerator(PasswordResetTokenGenerator):
    pass

# Define a single instance of the token generator
token_generator = CustomPasswordResetTokenGenerator()
def verify_email(request):
    email = request.GET.get("email")
    token = request.GET.get("token")
    try:
        user = CustomUser.objects.get(email=email)
    except CustomUser.DoesNotExist:
        messages.error(request, "Invalid verification link.")
        return redirect("home")
    if token_generator.check_token(user, token):
        user.is_active = True
        user.save()
        messages.success(request, "Your email has been verified!")
        return redirect("sign_in")
    else:
        messages.error(request, "Invalid or expired token.")
        return redirect("home")
from django.core.mail import send_mail
from django.urls import reverse
from user_management.utils import token_generator


def send_verification_email(user, request):
    token = token_generator.make_token(user)
    verification_url = request.build_absolute_uri(
        reverse("verify_email") + f"?token={token}&email={user.email}"
    )
    send_mail(
        "Verify your email",
        f"Click the link to verify your email: {verification_url}",
        "no-reply@example.com",
        [user.email],
        fail_silently=False,
    )

The best way for that is to using Django Allauth, You can do anything with that secure and easy, For sure Allauth have many other options for you.

The code you posted seems fine. The check_token method performs several checks and figuring out which exact one fails should lead you to a solution. You can add breakpoints or print statements in place where the Django package is installed or bring the code into your project. Since you're already subclassing PasswordResetTokenGenerator you can:

from django.conf import settings
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils.crypto import constant_time_compare
from django.utils.http import int_to_base36

class CustomPasswordResetTokenGenerator(PasswordResetTokenGenerator):
    def check_token(self, user, token):
        """
        Check that a password reset token is correct for a given user.
        """

        # Use breakpoints / print statements to figure out 
        # which of the conditions fails here and where to go next

        if not (user and token):
            return False
        # Parse the token
        try:
            ts_b36, _ = token.split("-")
        except ValueError:
            return False

        try:
            ts = base36_to_int(ts_b36)
        except ValueError:
            return False

        # Check that the timestamp/uid has not been tampered with
        for secret in [self.secret, *self.secret_fallbacks]:
            if constant_time_compare(
                self._make_token_with_timestamp(user, ts, secret),
                token,
            ):
                break
        else:
            return False

        # Check the timestamp is within limit.
        if (self._num_seconds(self._now()) - ts) > settings.PASSWORD_RESET_TIMEOUT:
            return False

        return True

# Define a single instance of the token generator
token_generator = CustomPasswordResetTokenGenerator()
Back to Top