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 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()