6

I have been trying to implement the django-otp with qrcode using custom Forms and Views. The problem is I am a little caught up on whether my implementation is correct or not. As the documentation states that a request.user.is_verified() attribute is added to users who have been OTP verified, I am actually not able to get it right. Have created confirmed TOTP device for user with the QR code setup using Microsoft Authenticator app.

I was able to successfully implement the default Admin Site OTP verification without any issues. Below is the files for the custom implementation.

urls.py

from django.conf.urls import url
from account.views import AccountLoginView, AccountHomeView, AccountLogoutView

urlpatterns = [
    url(r'^login/$', AccountLoginView.as_view(), name='account-login',),
    url(r'^home/$', AccountHomeView.as_view(), name='account-home',),
    url(r'^logout/$', AccountLogoutView.as_view(), name='account-logout',)
]

views.py

from django.contrib.auth import authenticate, login as auth_login
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
from django_otp.forms import OTPAuthenticationForm

class AccountLoginView(FormView):

    template_name = 'login.html'
    form_class = OTPAuthenticationForm
    success_url = '/account/home/'

    def form_invalid(self, form):
        return super().form_invalid(form)

    def form_valid(self, form):

        # self.request.user returns AnonymousUser
        # self.request.user.is_authenticated returns False
        # self.request.user.is_verified() returns False

        username = form.cleaned_data.get('username')
        password = form.cleaned_data.get('password')
        otp_token = form.cleaned_data.get('otp_token')
        otp_device = form.cleaned_data.get('otp_device')

        user = authenticate(request=self.request, username=username, password=password)

        if user is not None:

            device_match = match_token(user=user, token=otp_token)

            # device_match returns None

            auth_login(self.request, user)

            # self.request.user returns admin@mywebsite.com
            # self.request.user.authenticated returns True
            # self.request.user.is_verified returns AttributeError 'User' object has no attribute 'is_verified'
            # user.is_verified returns AttributeError 'User' object has no attribute 'is_verified'

        return super().form_valid(form)

class AccountHomeView(TemplateView):
    template_name = 'account.html'

    def get(self, request, *args, **kwargs):

        # request.user.is_authenticated returns True
        # request.user.is_verified() returns False

        return super(AccountHomeView, self).get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['data'] = 'This is secured text'
        return context

login.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
        <form action="." method="post">

            {% csrf_token %}

            {{ form.non_field_errors }}

            <div class="fieldWrapper">
                {{ form.username.errors }}
                <label for="{{ form.username.id_for_label }}">{{ form.username.label_tag }}</label>
                {{ form.username }}
            </div>

            <div class="fieldWrapper">
                {{ form.password.errors }}
                <label for="{{ form.password.id_for_label }}">{{ form.password.label_tag }}</label>
                {{ form.password }}
            </div>

            {% if form.get_user %}
                <div class="fieldWrapper">
                    {{ form.otp_device.errors }}
                    <label for="{{ form.otp_device.id_for_label }}">{{ form.otp_device.label_tag }}</label>
                    {{ form.otp_device }}
                </div>
            {% endif %}

            <div class="fieldWrapper">
                {{ form.otp_token.errors }}
                <label for="{{ form.otp_token.id_for_label }}">{{ form.otp_token.label_tag }}</label>
                {{ form.otp_token }}
            </div>

            <input type="submit" value="Log In" />

            {% if form.get_user %}
                <input type="submit" name="otp_challenge" value="Get Challenge" />
            {% endif %}

        </form>
    </body>
</html>

Could anyone please let me know what is that I am missing. I want to be able to authenticate and verify them using my own views by using the existing OTP form classes.

Please advice.

Fahim Ahmed
  • 317
  • 3
  • 14

0 Answers0