I am currently using the default Django auth system for logging in the user, I want to modify it so that instead of the default auth url executing it’s own login function, I want to add some additional steps to the login system.
Basically I want to generate an otp, a randomized 6 digit number and mail it to the email of the user who attempted to login, provided the users entered details were authenticated and verified using the default authenticate method.
After the user clicks the login button, if their entered data is authenticated, the email will be sent with an otp, and the user will be shown a page to enter the otp and verify it.
Once the otp is verified, the user is redirected to another page which shows the user some messages, what these messages are is not important, what is important is that user must be logged in AFTER the messages have been displayed, there must also be a conditional statement which returns the user to the main page if for whatever reason the messages were not shown. The messages will be coming from an API, and depending on the message the user will either be logged in or returned to the main home page.
Only then should the user be logged in completely, and be allowed to access urls decorated with the logged in decorator.
You can create a custom login function. here I’m sharing a code snippet for your reference
from django.contrib.auth.models import User
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib import messages
def login_view(request):
if request.method == "POST":
username = request.POST.get("username").lower()
password = request.POST.get("password")
try:
user = User.objects.get(username=username)
except:
messages.error(request, "User Not Found....")
return redirect("home")
if user is not None:
login(request, user)
return redirect("home")
else:
messages.error(request, "Username or Password does not match...")
return render(request, "login.html")
original user log in form for this you can inherit it like this and use it to pass from the view, also you can create a complete custom form for this as well.
from django import forms
from django.contrib.auth.forms import AuthenticationForm, UsernameField
class LoginForm(AuthenticationForm):
username = UsernameField(label='Enter Username', widget=forms.TextInput(attrs={'class':'form-control'}))
password = forms.CharField(label='Enter Password', widget=forms.PasswordInput(attrs={'class':'form-control'}))
def confirm_login_allowed(self, user):
if user.is_staff and not user.is_superuser:
raise ValidationError(
("This account is not allowed here."),
code='not_allowed',
)
I would just like to use the inbuilt login form, like there is one for user registration, the login form normally only has the username and password fields
I would also like to know that if I redirect to another view Inside the login view, how can i save the current users data so that i can login the user using the built in log in function from another view function.
My current view Implementation:
views.py
def sendOTP(email):
otp = None
# send random otp to email
otp = random.randint(100000, 999999)
subject = 'RPi Security System OTP'
message = 'Your OTP is ' + str(otp) + '. Please enter this OTP to login to your account.'
from_email = 'example@example.com'
recipient_list = [f'{email}']
send_mail(subject, message, from_email, recipient_list)
return otp
def login(request):
if request.method == "POST":
form = AuthenticationForm(request.POST)
if form.is_valid():
form.save()
# authenticate user
try:
username = request.POST.get('username')
password = request.POST.get('password')
email = request.POST.get('email')
user = authenticate(username=username, password=password)
request.session['username'] = username
request.session['email'] = email
except:
messages.error(request, f'Username {username} does not exist')
return redirect("login")
if user is not None:
otp = sendOTP(email)
if otp is not None:
request.session['otp_original'] = otp
return verifyOTP(request)
else:
messages.error(request, f'Username or password is incorrect')
return redirect("login")
else:
form = AuthenticationForm()
return render(request, 'users/login.html', {'form': form})
def verifyOTP(request):
if request.method == "POST":
otp = request.POST.get('otp')
form = OTPForm(request.POST)
if form.is_valid():
form.save()
if request.session['otp_original'] == int(otp):
return redirect('fingerprint')
else:
messages.error(request, f'OTP is incorrect')
return redirect('otp')
else:
form = OTPForm()
return render(request, 'users/otp.html', {'form': form})
# Change for actual code
def fingerprint(request):
return render(request, 'users/fingerprint.html')
First thing you don’t need to save the form in login form.save() because here you are only getting the data after form is submitted, this data is not going to be stored in your DB.
Now for your query, as I can see you first want to send the otp to the user then you want to login that user after otp is verified, so for storing the users current data like username you can use browser’s localstorage. Set username to localstorage once your work is done remove username from localstorage.
Can you also help me fix the login view, currently it is always returning “invalid form input”
views.py
def login(request):
if request.method == "POST":
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
email = user.email
# Store user ID in session, not the user object
request.session['user_id'] = user.id
otp = sendOTP(email)
request.session['otp_original'] = otp
return redirect("success_page") # Redirect to a success page or wherever needed
else:
messages.error(request, 'Invalid username or password')
else:
messages.error(request, 'Invalid form input')
else:
form = LoginForm()
return render(request, 'users/login.html', {'form': form})