I am trying to make a django app in which I want to create a opt verification but I am confused what is the right approach to do it. Here's what I have done so far:
Models.py
class User(AbstractUser):
is_shipper = models.BooleanField(default=False)
is_ftlsupp = models.BooleanField(default=False)
is_ptlsupp = models.BooleanField(default=False)
otp = models.IntegerField(default=1620122)
verified = models.BooleanField(default=False)
Serializers.py
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = "__all__"
read_only_fields = ('id', 'verified')
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
def random_with_N_digits(n):
range_start = 10**(n-1)
range_end = (10**n)-1
return randint(range_start, range_end)
otp = random_with_N_digits(6)
user.otp = otp
user.save()
subject = 'Please Confirm Your Account'
message = 'Your 6 Digit Verification Pin: {}'.format(otp)
email_from = '*****'
recipient_list = [str(user.email), ]
send_mail(subject, message, email_from, recipient_list)
return user
How can I use this otp to verify the user?
My approach is that if the user is created and he tries to login then obviously he is unverified as verified = models.BooleanField(default=False)
so he'd be shown a pop up to enter the otp he received on his mail and if the otp matches he can proceed and log-in
Views.py
To verify otp
class verifyOTPView(APIView):
def post(self, request):
username = request.data["username"]
otp = int(request.data["otp"])
user = User.objects.get(username=username)
if int(user.otp)==otp:
user.verified = True
#user.otp.delete() #?? How to handle the otp, Should I set it to null??
user.save()
return Response("Verification Successful")
else:
raise PermissionDenied("OTP Verification failed")
Please suggest how should I proceed and would it be wise to use the same otp
field for Resetting the password?
First, create a PhoneOTP model
class PhoneOTP(models.Model):
username = models.CharField(max_length=254, unique=True, blank=True, default=False)
phone_regex = RegexValidator( regex = r'^\+?1?\d{9,14}$', message = "Phone number must be entered in the form of +919999999999.")
name = models.CharField(max_length=254, blank=True, null=True)
phone = models.CharField(validators = [phone_regex], max_length=17)
otp = models.CharField(max_length=9, blank=True, null=True)
count = models.IntegerField(default=0, help_text = 'Number of opt_sent')
validated = models.BooleanField(default=False, help_text= 'if it is true, that means user have validate opt correctly in seconds')
def __str__(self):
return str(self.phone) + ' is sent ' + str(self.otp)
Then create an OTP generate view and verify view
class ValidatePhoneSendOTP(APIView):
permission_classes = (permissions.AllowAny, )
def post(self, request, *args, **kwargs):
name = request.data.get('name' , False)
phone_number = request.data.get('phone')
if phone_number:
phone = str(phone_number)
user = User.objects.filter(phone__iexact = phone)
if user.exists():
return Response({
'status' : False,
'detail' : 'Phone number already exists.'
})
else:
key = send_otp(phone)
if key:
old = Customer.objects.filter(phone__iexact = phone)
if old.exists():
old = old.first()
count = old.count
# if count > 20:
# return Response({
# 'status': False,
# 'detail' : 'Sending otp error. Limit Exceeded. Please contact customer support.'
# })
old.count = count + 1
old.save()
print('Count Increase', count)
return Response({
'status' : True,
'detail' : 'OTP sent successfully.'
})
else:
PhoneOTP.objects.create(
# name = name,
phone = phone,
otp = key,
)
link = f'API-urls'
requests.get(link)
return Response({
'status' : True,
'detail' : 'OTP sent successfully.'
})
else:
return Response({
'status' : False,
'detail' : 'Sending OTP error.'
})
else:
return Response({
'status' : False,
'detail' : 'Phone number is not given in post request.'
})
def send_otp(phone):
if phone:
key = random.randint(999,9999)
print(key)
return key
else:
return False
class ValidateOTP(APIView):
permission_classes = (permissions.AllowAny, )
def post(self, request, *args, **kwargs):
phone = request.data.get('phone' , False)
otp_sent = request.data.get('otp', False)
if phone and otp_sent:
old = Phone.objects.filter(phone__iexact = phone)
if old.exists():
old = old.first()
otp = old.otp
if str(otp_sent) == str(otp):
old.validated = True
old.save()
return Response({
'status' : True,
'detail' : 'OTP mactched. Please proceed for registration.'
})
else:
return Response({
'status' : False,
'detail' : 'OTP incorrect.'
})
else:
return Response({
'status' : False,
'detail' : 'First proceed via sending otp request.'
})
else:
return Response({
'status' : False,
'detail' : 'Please provide both phone and otp for validations'
})
In this way you can do verification of user by OTP
models.py:
class CustomUser(AbstractUser):
# username_validator = UnicodeUsernameValidator()
username = models.CharField(max_length=80,unique=True)
email = models.EmailField(unique=True)
otp = models.IntegerField(null=True,blank=True)
activation_key = models.CharField(max_length=150,blank=True,null=True)
urls.py:
urlpatterns = [
path('signup/', signup,name = "sign_up"),
path('signup_verify/<int:otp>/', signupVerify,name = "signup_verify"),
]
pip install pyotp
Docs : pyotp
views.py:
from rest_framework import status
from django.contrib.auth import get_user_model
from .serializers import SignUpSerializer
from rest_framework.decorators import api_view, permission_classes
import pyotp
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
from django.core.mail.message import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
User = get_user_model()
class generateKey:
@staticmethod
def returnValue():
secret = pyotp.random_base32()
totp = pyotp.TOTP(secret, interval=86400)
OTP = totp.now()
return {"totp":secret,"OTP":OTP}
@api_view(['POST'])
@permission_classes([AllowAny,])
def signup(request):
serializer = SignUpSerializer(data=request.data)
if serializer.is_valid():
key = generateKey.returnValue()
user = User(
username = serializer.data['username'],
email = serializer.data['email'],
otp = key['OTP'],
activation_key = key['totp'],
)
try:
validate_password(serializer.data['password'], user)
except ValidationError as e:
return Response(str(e), status=status.HTTP_400_BAD_REQUEST)
user.set_password(serializer.data['password'])
user.is_active = False
user.save()
email_template = render_to_string('signup_otp.html',{"otp":key['OTP'],"username":serializer.data['username']})
sign_up = EmailMultiAlternatives(
"Otp Verification",
"Otp Verification",
settings.EMAIL_HOST_USER,
[serializer.data['email']],
)
sign_up.attach_alternative(email_template, 'text/html')
sign_up.send()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])
@permission_classes([AllowAny,])
def signupVerify(request,otp):
try:
user = User.objects.get(otp = otp,is_active = False)
_otp = user.otp
if otp != _otp:
return Response({"Otp" : "Invalid otp"},status=status.HTTP_406_NOT_ACCEPTABLE)
else:
activation_key = user.activation_key
totp = pyotp.TOTP(activation_key, interval=86400)
verify = totp.verify(otp)
if verify:
user.is_active = True
user.save()
email_template = render_to_string('signup_otp_success.html',{"username":user.username})
sign_up = EmailMultiAlternatives(
"Account successfully activated",
"Account successfully activated",
settings.EMAIL_HOST_USER,
[user.email],
)
sign_up.attach_alternative(email_template, 'text/html')
sign_up.send()
return Response({"Varify success" : "Your account has been successfully activated!!"}, status=status.HTTP_202_ACCEPTED)
else:
return Response({"Time out" : "Given otp is expired!!"}, status=status.HTTP_408_REQUEST_TIMEOUT)
except:
return Response({"No User" : "Invalid otp OR No any inactive user found for given otp"}, status=status.HTTP_400_BAD_REQUEST)
signup_otp.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Otp Verification</title>
</head>
<body>
<h1>Otp Verification</h1>
<hr>
<small>Hello, {{username}}</small>
<p>Your Otp is <span style="font-weight: bolder; font-size: larger; background-color: rgb(230, 233, 236); padding: 4px;">{{otp}}</span></p>
<p>This otp is valid for 1 day only..</p>
<em>Thank you</em><br/>
<em>Team <b>Company name</b></em>
</body>
</html>
signup_otp_success.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account successfully activated</title>
</head>
<body>
<h1>Welcome <strong>{{username}}</strong></h1>
<hr>
<p>Your account successfully activated.Now you can access all the feature of this site!!</p>
<h6>Have a great day {{username}}</h6>
<small>Team Company Name.</small>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With