Email Verification tokens are not being properly verified
My code to verify users via their email after registering has a problem I can't pinpoint. Once successfully registered, a user receives an email with a link and a token attached. The token is then meant to be verified as valid, or flagged as invalid or expired. The emails are sent successfully. However, both expired and new tokens are also being flagged as invalid. What am I doing wrong?
This is the user model in my models.py file:
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
objects = UserManager()
def __str__(self):
return self.email
This is my serializers.py file:
from rest_framework import serializers
from .models import User
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=8, write_only=True)
class Meta:
model = User
fields = ["email", "username", "password"]
def validate(self, attrs):
email = attrs.get("email", "")
username = attrs.get("username", "")
if not username.isalnum():
raise serializers.ValidationError(
"The username should only contain alphanumeric characters"
)
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class VerifyEmailSerializer(serializers.ModelSerializer):
token = serializers.CharField(max_length=555)
class Meta:
model = User
fields = ["token"]
views.py
import jwt
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.shortcuts import render
from django.urls import reverse
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework import generics, status, views
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from .models import User
from .serializers import *
from .utils import Util
class RegisterView(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user = User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
relativeLink = reverse('email-verify')
absurl = 'http://'+current_site+relativeLink+"?token="+str(token)
email_body = 'Hi '+user.username + \
' Use the link below to verify your email \n' + absurl
data = {'email_body': email_body, 'to_email': user.email,
'email_subject': 'Verify your email'}
Util.send_email(data)
return Response(user_data, status=status.HTTP_201_CREATED)
class VerifyEmailView(views.APIView):
serializer_class = VerifyEmailSerializer
@extend_schema(
parameters=[
OpenApiParameter(
name="token", description="Description",
required=False, type=str, location=OpenApiParameter.QUERY
)])
def get(self, request):
token = request.GET.get('token')
try:
payload = jwt.decode(token, settings.SECRET_KEY)
user = User.objects.get(id=payload['user_id'])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({'email': 'Successfully activated'}, status=status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({'Error': 'Token Expired'}, status=status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({'Error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
and the file doing the sending (utils.py)
from django.core.mail import EmailMessage
class Util:
@staticmethod
def send_email(data):
email = EmailMessage(
subject=data["email_subject"], body=data["email_body"], to=[data["to_email"]])
email.send()
In the VerifyEmailView
, you're not specifying the algorithm used for decoding the JWT.
Your code has to be updated like this -
jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])