DEV Community

Cover image for How to Develop an Email-Verification API using Django Rest Framework
Noel Ethan Chiwamba
Noel Ethan Chiwamba

Posted on

How to Develop an Email-Verification API using Django Rest Framework

Introduction

An email is often times if not always an integral part of the credentials that a user is required to submit when signing up for a particular system or site. Many sites prompts their users to submit their emails on signup for various purposes since emails are fast , efficient and simple.

Many sites also use an email for user authentication and originality so it is important to verify user emails or in other words to check if the email submitted by the user is legally valid and functional before the user starts to access any functionality of your site.

Verifying user emails has many advantages, for example:

  • It helps your site to have real users
  • It eases communication with your system users
  • It eases the process of password reset and password change

SECTION 1

Prerequisites

In this article I expect you to have basic knowledge on
Django and Django Rest Framework and you're able to set up the environment using the python pip or pipenv.

Installing Dependencies

I assume you have a Django project that's already set and now we can **install **the following dependencies to have our project ready for our main functionality of email verification.

Therefore using your terminal to install the following

1-Django rest framework

$pip install djangorestframework  
Enter fullscreen mode Exit fullscreen mode

This will help develop a rest API that can be consumed by a site or a mobile app

2-Django rest framework simple JWT

pip install djangorestframework-simplejwt
Enter fullscreen mode Exit fullscreen mode

This package provides JSON Web Token Authentication support for Django REST framework. It will help us by generating tokens for every signed in client in the form of encoded JSON.

3-Django Rest SWagger Generator

$pip install -U drf-yasg
Enter fullscreen mode Exit fullscreen mode

This package will provide us with a user interface that will help us to interact with our email verification API

After installing these dependencies, if you already have a project with the user accounts application already created then you may go to the application models else if you don't have the user model already set may now create a django application in your project for users using the command python manage.py startapp <app name> in our case our app name will be accounts

Let's begin our coding....

Adding dependencies to Settings.py

Navigate to your project settings an add all the dependencies and the application we just created in our project in the python installed_apps

...
INSTALLED_APPS = [
     ...
    'rest_framework',
    'rest_framework_simplejwt',
    'drf_yasg',
    'accounts',
]
...
Enter fullscreen mode Exit fullscreen mode

User Model

If you already have a project its possible you have already defined your user model hence you may just add this field in the already existing user model or table then makemigrations and migrate your database

   ...
    #other user model fields
    is_verified = models.BooleanField(default=False)
   ...
Enter fullscreen mode Exit fullscreen mode

Else if you don't have a user model already defined you can define one by adding this code to your accounts/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
from rest_framework_simplejwt.tokens import RefreshToken

class User(AbstractUser):
    phone_number = models.IntegerField(blank=True, null=True)
    email = models.EmailField(unique=True, blank=False, null=False)
    is_verified = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def tokens(self):
        refresh = RefreshToken.for_user(self)
        return({
            'refresh': str(refresh),
            'refresh': str(refresh.access_token),
        })
Enter fullscreen mode Exit fullscreen mode

Code Explanation

Our User model is inheriting from the AbstractUser class which is a built in user model that contains all relevant fields for a user including username field, last_name, first_name, etc fields. For more info about the class AbstractUser click here

We also have overwritten the email field, (the field is already in the model) inorder for it to be unique by placing a unique=True attribute and added new field is_verified and phone_number

The tokens function is generating and returning tokens for any user that has been created.

After this then configure the model to the settings.py, then makemigrations and migrate your database.

SECTION 2

Creating Serializers

As a tradition in Django Rest Framework create a serializers.py file in your application in this case our accounts application.
The serializer will help us to convert the data objects available in our database into json objects that are easily consumable and transported by the web.

We will declare the fields that we want to serialize and also we will make the password a write only instance since a password is a very sensitive piece of data.

from rest_framework import serializers
from .models import User

class SignUpSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only =True  )
    # tokens = serializers.SerializerMethodField()
    class Meta:
        model = User
        fields = ['id', 'first_name', 'last_name',
                  'username', 'email', 'password', 'phone_number','tokens']
        read_only_fields= ['id',]

Enter fullscreen mode Exit fullscreen mode

Utils Class for Sending Email in Django

Lets create a file that will have a class that will help us in sending an email in our application. This file will be called utils.py and will use the django EmailMessage class to send the email.

Ofcourse right now the emails cannot be sent , we have one more configuration to go through, we will look at it at the end of the article.

from django.core.mail import EmailMessage
import threading

class EmailThread(threading.Thread):

    def __init__(self, email):
        self.email = email
        threading.Thread.__init__(self)

    def run(self):
        self.email.send()

class Util:
    @staticmethod
    def send_email(data):
        email = EmailMessage(
            subject=data['email_subject'], body=data['email_body'], to=[data['to_email']])
        EmailThread(email).start()
Enter fullscreen mode Exit fullscreen mode

Creating Views

Here in the views we will work on the post request which is a sign up request. The view will receive and validate all the data the user has submitted including the password and email and save the data into the User database table and send an email to the user confirming the sign up.

The email will also have a link that will take the user back to the API's verification view. The link will have a token that will be used to authenticate the user.

Remember the utils.py file created above will be imported here and data will be passed to the EmailMessage class to send the email to the just signed up user for verification.

# /views.py
from rest_framework.generics import ListAPIView, GenericAPIView
from rest_framework import response,status
from . import serializers, models
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.sites.shortcuts import get_current_site
from django.urls import reverse
import jwt
from .utils import Util


class SignUp(GenericAPIView):

    serializer_class = serializers.SignUpSerializer

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

        # getting tokens
        user_email = models.User.objects.get(email=user['email'])
        tokens = RefreshToken.for_user(user_email).access_token
        # send email for user verification
        current_site = get_current_site(request).domain
        relative_link = reverse('email-verify')
        absurl = 'http://'+current_site+relative_link+"?token="+str(tokens)
        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=data)

        return response.Response({'user_data': user, 'access_token' : str(tokens)}, status=status.HTTP_201_CREATED)

Enter fullscreen mode Exit fullscreen mode

From the above code, the user data have been saved and an access token is generated and concatenated to the API's url which has also been programmatically retrieved using the get_current_site(request).domain function that return the current site's URL. Altogether this data is sent to the email using the the Utils class that we created earlier.

The relative link is being assigned to a function reverse(email-verify). The reverse function will redirect the user to this 'email-verify' URL if the user successfully receives the email and clicks the link.

Email Verification

After the sign up and email processing, We need a view that will help us to verify the user's email after he clicks on the link that will be sent through the email.

but before that lets head to the serilaizers.py file and create a serializer for this view.

class SignUpSerializer(serializers.ModelSerializer):
 ... 
#already created!

class EmailVerificationSerializer(serializers.ModelSerializer):
    token = serializers.CharField(max_length=555)
    class Meta:
        model = User
        fields = ['token']
Enter fullscreen mode Exit fullscreen mode

After the serializers then we can head on to the views and verify the email.

... #other imports
from django.conf import settings
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema

class VerifyEmail(GenericAPIView ):
    serializer_class = serializers.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, options={"verify_signature": False})
            print(payload)
            user = models.User.objects.get(id=payload['user_id'])
            if not user.is_verified:
                user.is_verified = True
                user.save()
            return response.Response({'email': 'Successfully activated'}, status=status.HTTP_200_OK)
        except jwt.ExpiredSignatureError as identifier:
            return response.Response({'error': 'Activation Expired'}, status=status.HTTP_400_BAD_REQUEST)
        except jwt.exceptions.DecodeError as identifier:
            return response.Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
Enter fullscreen mode Exit fullscreen mode

Code explanation

When the user clicks on the Link sent to his email he will be redirected to this view and this view will get the token which is part of the URL. The token is decoded using the jwt.decode(token..). The user_id is retrieved from the decoded payload and theverified if he is in the database. If the user is in the database, the is_verified instance is checked if its false and the set to True and saved and a message is sent to the user that the user has beedn succefully verified and this means the email is functional.

Creating application URLs

from django.urls import path
from . import views

urlpatterns = [
    path('', views.SignUp.as_view() , name='signup' ),
    path('email-verify/', views.VerifyEmail.as_view(), name="email-verify"),  
]
Enter fullscreen mode Exit fullscreen mode

Creating Project Level URLs

These URLs will have a swagger schema since we installed the swagger generator to help us organize our API

from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from django.contrib import admin
from django.urls import path, include
from .swagger import BothHttpAndHttpsSchemaGenerator
import json

schema_view = get_schema_view(
   openapi.Info(
      title="Email-Verify API",
      default_version='v1',
      description="An API that will allow users to send sign up and the API will verify there emails by sending the emails usung the email submitted on sign up  ",

      terms_of_service="https://www.google.com/policies/terms/",
      contact=openapi.Contact(email="user@email.com"),
      license=openapi.License(name="BSD License"),
   ),

   permission_classes=[permissions.AllowAny],
)

urlpatterns = [
    path('admin/', admin.site.urls),
   path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    path('accounts/', include('accounts.urls'))
]

Enter fullscreen mode Exit fullscreen mode

SECTION 3

This section will cover the configuration of our API to be able to send emails our API to. We will use the google mail service. (gmail)

Configuring the settings.py

For us to be able to send the email we need to configure the settings.py with the below code and credentials for our gmail account.

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = ******@gmail.com
EMAIL_HOST_PASSWORD = *********
Enter fullscreen mode Exit fullscreen mode

Finally we may run the server for our API python manage.py runserver and see something like this:

API schema

When you click accounts and click the try it out button on email you can put any active email that you want try out the API.

Sending the user data to the API
and click the execute button and listen to check your email inbox

Here is an email that I received from the API to the email that i used

email fro API

LINKS

You may also find the code and the project on:
Github using this link - https://github.com/NoelEthanC/Email-Verify-APIs

You may view the API on raleway and try it out
Raleway Link: https://email-verify-api.up.railway.app/

Top comments (2)

Collapse
 
alivepillows profile image
Alya Izza

why my token can't send to my email?

Collapse
 
noel_ethan profile image
Noel Ethan Chiwamba

Hi Alya, please check if you have installed all the libraries used in this article, if you have installed all please make sure you have added the libraries to the settings.py file inside the your project folder.

If the problem persists please let me know.

Thank you.