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
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
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
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',
]
...
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)
...
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),
})
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',]
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()
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)
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']
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)
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"),
]
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'))
]
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 = *********
Finally we may run the server for our API python manage.py runserver
and see something like this:
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.
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
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)
why my token can't send to my email?
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.