How to Develop an Email-Verification API using Django Rest Framework
Tags: api, djangorestframework, emailverify
Published: 2022-12-24 09:23:37

## **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](https://www.django-rest-framework.org/#installation)
```python
$pip install djangorestframework  
``` 
> This will help develop a rest API that can be consumed by a site or a mobile app

### 2-[Django rest framework simple JWT](https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html)
```python
pip install djangorestframework-simplejwt
```
> 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](https://drf-yasg.readthedocs.io/en/stable/readme.html#installation)

```python
$pip install -U drf-yasg
```
> 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`
```python
...
INSTALLED_APPS = [
     ...
    'rest_framework',
    'rest_framework_simplejwt',
    'drf_yasg',
    'accounts',
]
...
```


## **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

```python
   ...
    #other user model fields
    is_verified = models.BooleanField(default=False)
   ...
```

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

```python
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),
        })
```
### 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](https://docs.djangoproject.com/en/4.1/topics/auth/customizing/)

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.


```python 
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',]
      
```

## **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.

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

##  **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.

```python 
# /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)

```

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.

```python
class SignUpSerializer(serializers.ModelSerializer):
 ... 
#already created!
        
class EmailVerificationSerializer(serializers.ModelSerializer):
    token = serializers.CharField(max_length=555)
    class Meta:
        model = User
        fields = ['token']
```
After the serializers then we can head on to the views and verify the email.

```python
... #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)
```

#### 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**

```python
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"),  
]
```
## **Creating Project Level URLs**
These URLs will have a swagger schema since we installed the swagger generator to help us organize our API 
```python
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'))
]

```

#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. 

```python
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 = *********
```
Finally we may run the server for our API `python manage.py runserver` and see something like this:


![API schema ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64d6c8otp7lvpazk20u1.JPG)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akhhlwmlcr9jw80aty41.JPG)
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7u2btpyslbj1ojmvy5q6.JPG)

## LINKS
You may also find the code and the project on:
[Github ](https://github.com/NoelEthanC/Email-Verify-APIs)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/) https://email-verify-api.up.railway.app/