Бэкэнд аутентификации 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.