- Project Setup
- Configure JWT Token
- Create Serializer Class
- Create View Methodology
- Email Configuration Documentations
- At first Create Environment:
python-m venv env
- After Create envirionment activate the environment:
.\env\Scripts\activate
- Install Dependencies:
pip install -r requirements.txt
- Create a Djnago Project:
django-admin startproject djangojwt
- Change current directory to
djangojwt
folder:cd djangojwt
- Create a Django App:
django-admin startapp myapp
- Update
settings.py
to Addrest_framework
andapp_name
toINSTALLED_APPS
:INSTALLED_APPS = [ ...... ...... 'rest_framework', 'myapp', ]
-
Install Required Libraries
pip install djangorestframework-simplejwt
-
Update
settings.py
to Addrest_framework_simplejwt
inside theINSTALLED_APPS
:INSTALLED_APPS = [ ...... ...... 'rest_framework', 'rest_framework_simplejwt', ]
-
Then, your django project must be configured to use the library. In
settings.py
, addrest_framework_simplejwt.authentication.JWTAuthentication
to the list of authentication classes:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ) }
- After Implement the Model Migrate the database:
py manage.py makemigrations py manage.py migrate
- Create superuser
py manage.py createsuperuser
- Run the project
py manage.py runserver
To configure the serializer create serializers.py
file inside the app
:
- First create
UserSerializer
to get or store the user data:from rest_framework import serializers from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id','username','email','date_joined']
- Create
UserRegisterSerializer
class for register the user information:class UserRegisterSerializer(serializers.ModelSerializer): id = serializers.PrimaryKeyRelatedField(read_only=True) username = serializers.CharField() first_name = serializers.CharField() last_name = serializers.CharField() email = serializers.EmailField() password = serializers.CharField(write_only=True) confirm_password = serializers.CharField(write_only=True) class Meta: model = User fields = ['id','username','first_name','last_name','email','password','confirm_password']
- To validate the username create a function inside the
UserRegisterSerializer
class. This function check this user already exists or not.from rest_framework.exceptions import ValidationError def validate_username(self, username): if User.objects.filter(username=username).exists(): detail = { "detail": "User Already exist!" } raise ValidationError(detail=detail) return username
- To validate the password matching and also check email already exists or not.
def validate(self, instance): if instance['password'] != instance['confirm_password']: raise ValidationError({"message":"Both password must match"}) if User.objects.filter(email=instance['email']).exists(): raise ValidationError({"message":"Email already taken!"}) return instance
- After validate the user then implement a functio name create to create the user.
def create(self, validated_data): password = validated_data.pop('password') confirm_password = validated_data.pop('confirm_password') user = User.objects.create(**validated_data) user.set_password(password) user.save() return user
- Create user login serializer
class UserLoginSerializer(serializers.ModelSerializer): username = serializers.CharField() password = serializers.CharField(write_only=True) class Meta: model = User fields = ['username', 'password']
- Create user login otp serializer
class UserLoginOTPSerializer(serializers.Serializer): username = serializers.CharField() otp = serializers.CharField(max_length=6)
Create viewsets using diffent api view like: ModelViewSet
, GenericAPIView
, APIView
method. Also import required libries:
- Here I create user registration method using
ModelViewSet
with the custom@action
method for custom registration endpoint.#Import the libries from django.contrib.auth.models import User from rest_framework import status,viewsets from rest_framework.exceptions import MethodNotAllowed from .serializers import UserRegisterSerializer
class UserRegisterViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserRegisterSerializer
ModelViewSet
provide default create method. But here i need to customize the registration that's why applyMethodNotAllowed
. For that include the create function under theUserRegisterViewSet
class:def create(self, request, *args, **kwargs): raise MethodNotAllowed(method="POST",detail="User creation is not allowed on this endpoint. Go to '/users/register/' endpoint")
- Now apply the custom registratio function under the
UserRegisterViewSet
class:@action(methods=['post'], detail=False, url_path='register') def register(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) if serializer.is_valid(): user = serializer.save() response = { 'success': True, 'user': { 'id': user.id, 'username': user.username, 'email': user.email, 'first_name': user.first_name, 'last_name': user.last_name, } } return Response(response, status=status.HTTP_201_CREATED) raise ValidationError(serializer.errors, code=status.HTTP_400_BAD_REQUEST)
- Here include the user login view. With the login functionalities include the user
JWT
Authentication and Email based2FA OTP
authentication. Step by step process how a user can access their data into their dashboard or account:- Goto
/api/login/
endpoint and Login usingusername
andpassword
- After that send a
OTP
user registered email address. - To validate the OTP goto
/api/verify-otp/
endpoint and verify the otp using username and otp. - After validation generate a
refresh token
andaccess token
. Using theaccess token
user can access their data.
- Goto
- Before create login method configure the email configuration. Show the configuration here: Click Here
- Now create
Loginview
method. At first need to import some libries:Create an in-memory dictionaryfrom rest_framework.exceptions import ValidationError
otp_storage
to stored the OTP temporarily in an in-memory dictionary. It declare globally:otp_storage = {}
class LoginView(generics.GenericAPIView): serializer_class = UserLoginSerializer def post(self, request, *args, **kwargs): username = request.data.get("username") password = request.data.get("password") if not username or not password: raise ValidationError({"message": "Username and password are required."}) # Authenticate the user user = authenticate(username=username, password=password) if user is None: raise ValidationError({"message": "Invalid credentials"}) # Generate opt otp = get_random_string(length=6, allowed_chars='1234567890') otp_storage[username] = otp # Send OTP to user's email send_mail( subject="Your OTP for Login", message=f"Your OTP is {otp}. It is valid for 5 minutes.", from_email="[email protected]", recipient_list=[user.email], ) return Response({ "message": "OTP sent to your registered email address." }, status=status.HTTP_200_OK )
- This function used of verify the otp.
class VerifyOtpView(generics.GenericAPIView): serializer_class = UserLoginOTPSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) # Extract validated data username = serializer.validated_data.get('username') otp = serializer.validated_data.get('otp') # Check if OTP exists for the username and validate it if username not in otp_storage: raise ValidationError({"message": "OTP has not been sent or expired or username not valid."}) # Validate OTP if otp_storage[username] != otp: raise ValidationError({"message": "Invalid OTP."}) # Clean up OTP after verification del otp_storage[username] try: user = User.objects.get(username=username) except User.DoesNotExist: raise ValidationError({"message": "User does not exist."}) refresh = RefreshToken.for_user(user) access_token = str(refresh.access_token) user_serializer = UserSerializer(user) return Response( { 'access': access_token, 'refresh': str(refresh), 'user': user_serializer.data, }, status=status.HTTP_200_OK )
- Install
python-decouple
.It is a popular package for managing environment variables in Django.pip install python-decouple
- Create a
.env
file in the root directory of your project (where manage.py is located). This file will store your sensitive data.EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 EMAIL_USE_TLS=True EMAIL_USE_SSL=False EMAIL_HOST_USER=your-email@gmail.com EMAIL_HOST_PASSWORD=your-email-app-password
- Now how to get the email
EMAIL_HOST_PASSWORD
:- Go to the Manage Google Account
- If 2FA not enable at first enable the 2FA
- Then Search
App Passwords
. And go to this page. - Set any app name like: (e.g. Django App etc.)
- After that click create. Then it provide a 16-characters password. like this:
takv dcgx ldzo poll
. - Then copy it and and using on the project.
- Configure Email setting inside the
settings.py
:If fetch any error usingfrom decouple import config EMAIL_HOST = config('EMAIL_HOST', default='smtp.gmail.com') EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int) EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool) EMAIL_USE_SSL = config('EMAIL_USE_SSL', default=False, cast=bool) EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='')
from decouple import config
this package. Then used bellow method:from dotenv import load_dotenv import os # Email configuration from environment variables EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.gmail.com') EMAIL_PORT = int(os.getenv('EMAIL_PORT', 587)) EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'True') == 'True' EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'False') == 'True' EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '') EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
- Sending Email views:
from django.core.mail import send_mail from django.conf import settings def send_otp_email(user, otp): send_mail( subject="Your OTP for Login", message=f"Your OTP is {otp}. It is valid for 5 minutes.", from_email=settings.EMAIL_HOST_USER, recipient_list=[user.email], )