muyiwa. py
muyiwa. py

Reputation: 1

Upon email verification endpoint I'm getting an Invalid token error while trying to activate a user with tokens sent to the mail?

from django.db import models
from django.contrib.auth.models import (
    AbstractBaseUser, BaseUserManager, PermissionsMixin)
from rest_framework_simplejwt.tokens import RefreshToken


class UserManager(BaseUserManager):

    def create_user(self,username,email, password=None ):
        if username is None:
            raise TypeError("Users should have a username")
        if email is None:
            raise TypeError("Users should have an Email")

        user =self.model(username=username, email=self.normalize_email(email))
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, username, email, password=None):
        if password is None:
            raise TypeError("Password should not be none")

        user = self.create_user(username, email, password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user

class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=100, unique=True, db_index=True)
    email = models.EmailField(max_length=100, 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

    def tokens(self):
        refresh = RefreshToken.for_user(self)
        return {
            "refresh":str(refresh),
            "access": str(refresh.access_token)
        }

Below is the serializers.py file

from .models import User
from rest_framework import serializers
from django.contrib import auth
from rest_framework.exceptions import AuthenticationFailed

class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(max_length=50, min_length=6, 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 contain only alphanumeric characters")
                return attrs
        
        def create(self, validated_data):
            return User.objects.create_user(**validated_data)


class EmailVerificationSerializer(serializers.ModelSerializer):
    token = serializers.CharField(max_length=555)
    class Meta:
        model = User
        fields = ["token"]

class LoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(max_length=255,min_length=3)
    password = serializers.CharField(max_length=68, min_length=6, write_only=True)
    username = serializers.CharField(max_length=255,min_length=3, read_only=True)
    tokens = serializers.CharField(max_length=68, min_length=6,read_only=True)



    class Meta:
        model = User
        fields = ["email", "password","username","tokens"]

    def validate(self,attrs):
        email = attrs.get("email", "")
        password = attrs.get("password", "")

        user = auth.authenticate(email=email, password=password)
        if not user:
            raise AuthenticationFailed("Invalid Credentials, try again")
        if not user.is_active:
            raise AuthenticationFailed("Account disabled, contact admin")

        if not user.is_verified:
            raise AuthenticationFailed("Email is not verified")
     
       
        return super().validate(attrs,{
            "email":user.email,
            "username": user.username,
            "tokens":user.tokens
        })

Below is the views.py file

**from django.shortcuts import render from rest_framework import generics, status, views from .serializers import EmailVerificationSerializer, RegisterSerializer, LoginSerializer from rest_framework.response import Response

from rest_framework_simplejwt.tokens import RefreshToken, AccessToken

from .models import User

from .utils import Util

from django.contrib.sites.shortcuts import get_current_site

from django.urls import reverse

from django.conf import settings

from drf_yasg.utils import swagger_auto_schema

from drf_yasg import openapi

import jwt**

# Create your views here.
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 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 VerifyEmail(views.APIView):
    serializer_class = EmailVerificationSerializer

    token_param_config = openapi.Parameter(
        "token", in_=openapi.IN_QUERY, description="Description",type=openapi.TYPE_STRING)

    
    
    @swagger_auto_schema(manual_parameters=[token_param_config])
    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": "Activation Link Expired"}, status=status.HTTP_400_BAD_REQUEST)
        except jwt.exceptions.DecodeError as identifier:
            return Response({"error": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)


class LoginAPIView(generics.GenericAPIView):

    serializer_class = LoginSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)

        return Response(serializer.data, status=status.HTTP_200_OK)

Below is the utils.py for sending a mail

from django.core.mail import EmailMessage

class Util:
    @staticmethod  #We'll use the class method without instantiating it
    def send_email(data):
        
        email =EmailMessage(
            subject=data["email_subject"], body=data["email_body"], to=[data["to_email"]])
        email.send()

Below is the urls.py

from django.urls import path
from .views import RegisterView, VerifyEmail, LoginAPIView

urlpatterns = [
    path("register/", RegisterView.as_view(), name="register"),
    path("email-verify/", VerifyEmail.as_view(), name="email-verify"),
    path("login/", LoginAPIView.as_view(), name="login")
] 

Below is the verification mail sent

A verification mail sent

Below are the error gotten while trying to verify the token

A screenshot image description

Upvotes: 0

Views: 489

Answers (1)

kleidilamka
kleidilamka

Reputation: 1

at VerifyEmailView class the payload needs the algorithm with which you decode your token.`class VerifyEmail(views.APIView): serializer_class = EmailVerificationSerializer

token_param_config = openapi.Parameter(
    "token", in_=openapi.IN_QUERY, description="Description",type=openapi.TYPE_STRING)



@swagger_auto_schema(manual_parameters=[token_param_config])
def get(self,request):
    token = request.GET.get("token")
    
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms='HS256')
        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": "Activation Link Expired"}, status=status.HTTP_400_BAD_REQUEST)
    except jwt.exceptions.DecodeError as identifier:
        return Response({"error": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)`

Upvotes: 0

Related Questions