使用自定义视图的 django-otp 实现

Fah*_*med 7 django two-factor-authentication

我一直在尝试使用自定义表单和视图来实现带有qrcodedjango-otp。问题是我有点不明白我的实现是否正确。由于文档指出向已通过 OTP 验证的用户添加了一个属性,因此我实际上无法正确设置。已使用 Microsoft Authenticator 应用程序通过 QR 码设置为用户创建了已确认的 TOTP 设备。request.user.is_verified()

我能够成功实施默认的管理站点 OTP 验证,没有任何问题。以下是自定义实现的文件。

网址.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',)
]
Run Code Online (Sandbox Code Playgroud)

视图.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
Run Code Online (Sandbox Code Playgroud)

登录.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>
Run Code Online (Sandbox Code Playgroud)

任何人都可以让我知道我缺少什么。我希望能够通过使用现有的 OTP 表单类使用我自己的视图对它们进行身份验证和验证。

请指教。

Sim*_*sov 1

到底是什么match_token?您不需要设备字段,而是尝试为用户提供设备。

这是我的实现。

from django_otp import devices_for_user
from django_otp.plugins.otp_totp.models import TOTPDevice


def get_user_totp_device(user, confirmed=None):
    devices = devices_for_user(user, confirmed=confirmed)
    for device in devices:
        if isinstance(device, TOTPDevice):
            return device


def create_device_topt_for_user(user):
    device = get_user_totp_device(user)
    if not device:
        device = user.totpdevice_set.create(confirmed=False)
    return device.config_url


def validate_user_otp(user, data):
    device = get_user_totp_device(user)
    serializer = otp_serializers.TokenSerializer(data=data)

    if not serializer.is_valid():
        return dict(data='Invalid data', status=status.HTTP_400_BAD_REQUEST)
    elif device is None:
        return dict(data='No device registered.', status=status.HTTP_400_BAD_REQUEST)
    elif device.verify_token(serializer.data.get('token')):
        if not device.confirmed:
            device.confirmed = True
            device.save()
            return dict(data='Successfully confirmed and saved device..', status=status.HTTP_201_CREATED)
        else:
            return dict(data="OTP code has been verified.", status=status.HTTP_200_OK)
    else:
        return dict(
            data=
            dict(
                statusText='The code you entered is invalid',
                status=status.HTTP_400_BAD_REQUEST
            ),
            status=status.HTTP_400_BAD_REQUEST
        )
Run Code Online (Sandbox Code Playgroud)

然后在一个视图中,你可以做类似的事情

create_device_topt_for_user(user=request.user)

validate_user_otp(request.user, request.data)
Run Code Online (Sandbox Code Playgroud)