Бэкэнд аутентификации django игнорируется при указании

У меня есть два бэкенда аутентификации клиентов: один для стандартного входа, а другой для двухфакторного входа.

В моем файле settings.py они оба перечислены

AUTHENTICATION_BACKENDS = [
    'user_profile.auth_backends.TOTPBackend',
    'user_profile.auth_backends.StandardLoginBackend',
    'django.contrib.auth.backends.ModelBackend',
]

У меня они хранятся в папке app (user_profile) в файле auth_backends.py, и имена классов совпадают:

class TOTPBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, twofa_token=None, **kwargs):
        print("In TOTPBackend")
        # First, try to authenticate with the default method
        user = super().authenticate(request, username=username, password=password, **kwargs)
        
        if user is not None:
...

И

class StandardLoginBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        print("In StandardLoginBackend")

        # First, try to authenticate with the default method
        user = super().authenticate(request, username=username, password=password, **kwargs)
        
        if user is not None:
...

Затем, основываясь на представлении, вызываемом из некоторой логики, я хочу явно вызвать то или иное представление...

class TwoFaLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("TwoFaLogin View")
        username = request.data['username']
        passwd = request.data['password']
        twofa_token = request.data['twofa_token']

        user = authenticate(request, username=username, password=passwd, twofa_token=twofa_token, backend='user_profile.auth_backends.TOTPBackend')

ИЛИ

class StandardLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("StandardLogin View")
        username = request.data['username']
        passwd = request.data['password']
        user = authenticate(request, username=username, password=passwd, backend='user_profile.auth_backends.StandardLoginBackend')

Моя проблема заключается в том, что при выполнении вызова аутентификации из представления параметры бэкенда игнорируются - он всегда вызывает их в порядке следования настроек settings.py AUTHENTICATION_BACKENDS.

Я не могу найти никакого объяснения, кроме того, что это должно работать... Все советы, которые я нашел, говорят, что нужно проверить путь, что я и сделал, и он правильный. Если я меняю порядок в списке settings.py, это меняет порядок вызовов. Если нужный вызов находится сверху для нужной ситуации, все работает, как и ожидалось, но он не будет вызывать указанный бэкенд - он просто перечисляет список сверху вниз.

Кто-нибудь еще сталкивался с этим? Почему параметр backend при вызове authenticate не заставляет вызов использовать указанный мною backend?

Спасибо за любую помощь.

BCBB

Сначала,

Порядок AUTHENTICATION_BACKENDS имеет значение, поэтому если одно и то же имя пользователя и пароль действительны в нескольких бэкендах, Django остановит обработку на первом положительном совпадении.

Если бэкенд поднимает исключение PermissionDenied, аутентификация немедленно завершится неудачей. Django не будет проверять последующие бэкенды. Auth Backend Django Docs

.

Насколько я понял, вам нужны отдельные конечные точки для разных бэкендов аутентификации, и вы используете функцию django authenticate, и передаете ей backend, по умолчанию функция authenticate циклически проходит через все бэкенды, и когда получает PermissionDenied, она больше не делает итерацию и напрямую выбрасывает исключение. Реализация функции authenticate в Django

Вот решение 1:

Создайте пользовательскую authenticate функцию.

from django.contrib.auth import load_backend
from django.contrib.auth import authenticate as django_authenticate
from django.core.exceptions import PermissionDenied

def your_custom_authenticate(request=None, **credentials):
    backend = credentials.get('backend')
    if backend:
        auth_backend = load_backend(backend)
        user = auth_backend.authenticate(request, **credentials)
        if not user:
            raise PermissionDenied("Authentication Failed")
    django_authenticate(request=request, **credentials)

Тогда вы можете использовать эту функцию аутентификации на основе ваших представлений

class TwoFaLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("TwoFaLogin View")
        username = request.data['username']
        passwd = request.data['password']
        twofa_token = request.data['twofa_token']

        user = your_custom_authenticate(request, username=username, password=passwd, twofa_token=twofa_token, backend='user_profile.auth_backends.TOTPBackend')


class StandardLogin(APIView):
    permission_classes = (AllowAny,)

    def post(self, request):
        print("StandardLogin View")
        username = request.data['username']
        passwd = request.data['password']
        user = your_custom_authenticate(request, username=username, password=passwd, backend='user_profile.auth_backends.StandardLoginBackend')

В этом решении он не будет переходить к следующему бэкенду аутентификации, а сразу поднимет PermissionDenied.

Вернуться на верх