Search code examples
pythonhtmldjangopython-3.xone-time-password

User login and Django OTP


I want the user to login using three fields, username, password and OTP ONLY.

Here is my .html file

{% extends "base.html" %}
{% load static %}
{% load bootstrap4 %}
{% block head %}
    <title>HomePage</title>
{% endblock %}
{% block content %}
    <div class="container">
        <div style='width:500px;margin:0 auto;' class="panel panel-default">
            <div class="panel-body">
                <h1>Login</h1>
                <form method="POST" class="form">
                    {% csrf_token %}
                    {% bootstrap_form form %}
                    {% buttons %}
                        <button type="submit" class="btn btn-primary">Login</button>
                    {% endbuttons %}
                </form>
            </div>
        </div>
    </div>
{% endblock %}

Here is the browser view of the .html file

enter image description here

I want to remove Otp Device and Otp Challenge as they are unnecessary for my case.

Here is my models.py file

from django.contrib.auth.models import AbstractUser


class ProjectUser(AbstractUser):
    # add additional fields in here

    def __str__(self):
        return self.email

Here is my urls.py file

from django_otp.forms import OTPAuthenticationForm
from django.contrib.auth.views import LoginView

urlpatterns = [

    path('user_login/', LoginView.as_view(template_name="accounts_app/user_login.html",
                                         authentication_form=OTPAuthenticationForm), name='user_login'), ]

Here is the reference I used


Solution

  • Solution 1:

    The straight forward way is to Subclass OTPAuthenticationForm to replace the form fields used for otp_device and otp_challenge by using the HiddenInput widget. In your app folder, create a new python file called forms.py and add the following

    from django_otp.forms import OTPAuthenticationForm
    from django import forms
    
    class SimpleOTPAuthenticationForm(OTPAuthenticationForm):
        otp_device = forms.CharField(required=False, widget=forms.HiddenInput)
        otp_challenge = forms.CharField(required=False, widget=forms.HiddenInput)
    

    In your urls.py file inside your app, add the import and replace the LoginView with the following

    from .forms import SimpleOTPAuthenticationForm
    
    path('user_login/', LoginView.as_view(template_name="accounts_app/user_login.html",
                                          authentication_form=SimpleOTPAuthenticationForm), name='user_login'),
    

    Solution 2:

    Alternative option but requires more styling to fit the bootstrap form warning and other features. Start with using the following

    Only display the fields you want in your template: Since otp_device and otp_challenge are not required, you can just leave them out. Use {% bootstrap_field form.<field> %} for each of the fields you want to display instead of {% bootstrap_form form %}. See here for all the options to customise the rendering of each field. Errors for each field can be displayed with {{ form.errors.<field> }} but you have to style them yourself.

    In your case

    {% bootstrap_field form.username %}
    {% bootstrap_field form.password %}
    {% bootstrap_field form.otp_token %}