Django rest framework JWT и пользовательский бэкэнд аутентификации

У меня есть пользовательская модель пользователя, и я создал пользовательский бэкэнд аутентификации. Я использую http://www.django-rest-framework.org/ и django rest framework JWT для аутентификации токена.

Модель пользователя:

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(
        unique=True,
        max_length=254,
    )
    first_name = models.CharField(max_length=15)
    last_name = models.CharField(max_length=15)
    mobile = models.IntegerField(unique=True)
    date_joined = models.DateTimeField(default=timezone.now)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    objects = UserManager()
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name', 'mobile']

Бэкэнд аутентификации:

class EmailOrMobileAuthBackend(object):
    def authenticate(self, username=None, password=None):
        try:
            user = get_user_model().objects.get(email=username)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            if username.isdigit():
                try:
                    user = get_user_model().objects.get(mobile=username)
                    if user.check_password(password):
                        return user
                except User.DoesNotExist:
                    return None
            else:
                return None

    def get_user(self, user_id):
        try:
            return get_user_model().objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

И добавили в settings.py:

AUTHENTICATION_BACKENDS = ('accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',)

Несмотря на то, что для входа на сайт администратора django, адрес электронной почты и номер мобильного телефона отлично работают при аутентификации пользователя. Однако, когда я пытаюсь получить токен для пользователя, использующего django rest framework JWT, я получаю сообщение об ошибке:

curl -X POST -d "email=admin@gmail.com&password=123123" http://localhost/api-token-auth/

"non_field_errors": [
    "Unable to log in with provided credentials."
  ]

Я также добавил пользовательский базовый класс auth в среду проверки подлинности по умолчанию для Rest, но он все еще не работает:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

Что мне не хватает? Почему он работает при входе на сайт администратора django, но выдает ошибку при получении токена с django rest framework jwt?

Обновить

Я сделал еще один auth backend в соответствии с рекомендациями и добавил его в DEFAULT_AUTHENTICATION_CLASSES, но даже это не работает.

class DrfAuthBackend(BaseAuthentication):
    def authenticate(self, username=None, password=None):
        try:
            user = get_user_model().objects.get(email=username)
            if user.check_password(password):
                return user, None
        except User.DoesNotExist:
            if username.isdigit():
                try:
                    user = get_user_model().objects.get(mobile=username)
                    if user.check_password(password):
                        return user, None
                except User.DoesNotExist:
                    return None
            else:
                return None

Настройки:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAdminUser',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',
        'accounts.email_mobile_auth_backend.DrfAuthBackend',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

Обновить

Изменение args в классе аутентификации из username в email кажется, работает для получения auth_token но опять же не работает войти на сайт администратора.

class EmailOrMobileAuthBackend(object):
    def authenticate(self, email=None, password=None):
        try:
            user = get_user_model().objects.get(email=email)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            if email.isdigit():
                try:
                    user = get_user_model().objects.get(mobile=email)
                    if user.check_password(password):
                        return user
                except User.DoesNotExist:
                    return None
            else:
                return None

    def get_user(self, user_id):
        try:
            return get_user_model().objects.get(pk=user_id)
        except User.DoesNotExist:
            return None 

2 ответа

Вы должны проверить документацию DRF для пользовательских бэкэндов аутентификации.

Я думаю, что ваш пользовательский бэкэнд аутентификации ломает его, вы можете решить его, удалив свой из настроек DRF:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

Или исправив ваш, который в принципе не такой, как для пользовательской аутентификации Django, как вы должны расширить authentication.BaseAuthentication и он возвращает кортеж.

from rest_framework import authentication


class DrfAuthBackend(authentication.BaseAuthentication):
    def authenticate(self, email=None, password=None):
        try:
            user = get_user_model().objects.get(email=email)
            if user.check_password(password):
                return user, None
        except User.DoesNotExist:
            if email.isdigit():
                try:
                    user = get_user_model().objects.get(mobile=email)
                    if user.check_password(password):
                        return user, None
                except User.DoesNotExist:
                    return None
            else:
                return None

Затем используйте это в настройках DRF:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAdminUser',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'accounts.email_mobile_auth_backend.DrfAuthBackend',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

Для входа в Django:

class EmailOrMobileAuthBackend(object):
    def authenticate(self, username=None, password=None):
        try:
            user = get_user_model().objects.get(email=username)
            if user.check_password(password):
                return user
        except User.DoesNotExist:
            if username.isdigit():
                try:
                    user = get_user_model().objects.get(mobile=username)
                    if user.check_password(password):
                        return user
                except User.DoesNotExist:
                    return None
            else:
                return None

    def get_user(self, user_id):
        try:
            return get_user_model().objects.get(pk=user_id)
        except User.DoesNotExist:
            return None 

Тогда для настроек:

AUTHENTICATION_BACKENDS = ('accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend',)

В файле settings.py добавьте следующее:

AUTH_USER_MODEL = 'accounts.email_mobile_auth_backend.EmailOrMobileAuthBackend'

Другие вопросы по тегам