Firstly, the login.html should ask for 'Enter OTP' once the email(username) is entered and submitted. This username is checked with user table if the user exists. If user exists , it should send OTP to the mobile registered for this user instance. On entry of the OTP, user should get appropriate message to reset password or get the home page. I don't want to use the django-otp app. What i have done so far: In the django accounts/registration/templates/login.html
{% extends "admin/base_site.html" %}
{% load i18n static %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}">
{{ form.media }}
{% endblock %}
{% block bodyclass %}{{ block.super }} login{% endblock %}
{% block usertools %}{% endblock %}
{% block nav-global %}{% endblock %}
{% block content_title %}{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
{% if form.errors and not form.non_field_errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{% endif %}
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<p class="errornote">
{{ error }}
</p>
{% endfor %}
{% endif %}
<div id="content-main">
{% if user.is_authenticated %}
<p class="errornote">
{% blocktrans trimmed %}
You are authenticated as {{ username }}, but are not authorized to
access this page. Would you like to login to a different account?
{% endblocktrans %}
</p>
{% endif %}
<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
<div class="form-row">
{{ form.username.errors }}
{{ 'Email:' }} {{ form.username }}
</div>
<div class="form-row">
{{ form.password.errors }}
<!-- {{ form.password.label_tag }} {{ form.password }} -->
<input type="hidden" name="next" value="{{ next }}">
</div>
{% url 'admin_password_reset' as password_reset_url %}
{% if password_reset_url %}
{% comment %}
<div class="password-reset-link">
<a href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a>
</div>
{% endcomment %}
{% endif %}
<div>
{% trans 'Enter OTP' %}</a>
<input type="integer" name="otp" value="{{ otp }}">
</div>
<div class="submit-row">
<label> </label><input type="submit" value="{% trans 'Log in' %}">
</div>
</form>
</div>
{% endblock %}
AuthenticationForm:
from django import forms
from django.db.models import IntegerField
from django.contrib.auth.forms import AuthenticationForm, UsernameField
class AuthenticationForm(AuthenticationForm):
class Meta:
model = User
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AuthenticationForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.error_messages = {'required':'{fieldname} is required'.format(
fieldname=field.label)}
username = UsernameField(
label='Email',
widget=forms.TextInput(attrs={'autofocus': True})
)
otp = IntegerField()
myauthentication backend code:
from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.contrib.auth.models import User, Group
from myapp.models import *
from django.http import *
from datetime import datetime
from django.urls import reverse
from django.utils.translation import ugettext as _
from myapp.forms import AuthenticationForm
import pyotp
from rest_framework.response import Response
from rest_framework.views import APIView
import base64
def generateKey(phone):
return str(phone) + str(datetime.date(datetime.now())) + "Some Random Secret Key"
class EmailBackend(ModelBackend):
#@staticmethod
def authenticate(self, request, username=None, password=None, **kwargs):
mMobile = None
user = None
form = AuthenticationForm(request=request, data=request.GET)
if request.GET:
try:
#to allow authentication through phone number or any other
#field, modify the below statement
user = UserModel.objects.get(
Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
print('iiii')
UserModel().set_password(password)
except MultipleObjectsReturned:
print(222222)
user = User.objects.filter(email=username).order_by('id').first()
else:
if user.check_password(password) and self.user_can_authenticate(
user):
form = AuthenticationForm(request=request, data=request.POST)
if form['username']:
try:
mMobile = Mailbox.objects.get(email=form['username'].value())
#print(user,mMobile, 'uuu-mmmm', dir(mMobile))
except Exception as e:
print(e)
#return user
if mMobile:
mMobile.counter += 1 # Update Counter At every Call
mMobile.save() # SamMove the data
print(mMobile.mobile)
keygen = generateKey(mMobile.mobile)
# Key is generated
key = base64.b32encode(keygen.encode())
OTP = pyotp.HOTP(key)
motp = (OTP.at(mMobile.counter))
print(motp, 'oooottttpp')
if request.POST:
if str(motp) == form.data['otp']:
print(form.data['otp'],'ddddd')
return user
else:
return
return
# Using Multi-Threading send the OTP Using Messaging
# Services like Twilio or Fast2sms
#return user
else:
return user
def get_user(self, user_id):
try:
user = UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
Problem: on entering username and submitting, it should send otp sms. Right now it sends the otp when the form is submitted. How exactly to incorporate this wherein the login form can be resubmitted 2 times? Using django 3.1, postgresql database. I need the exact code Some parts like otp generation I have used from here: [https://github.com/Akash16s/OTP-in-django][1]
[1]: https://github.com/Akash16s/OTP-in-django Feel stuck. How to go about?