django-rest-framework-jwt
django-rest-framework-jwt copied to clipboard
`JSONWebTokenSerializer` always return "Unable to log in with provided credentials." when the user is not activated.
My environment as follows:
- macOS Sierra 10.12.6
- Python 3.6.0
- Django 2.0.5
- djangorestframework 3.8.2
- djangorestframework-jwt 1.11.0
What is expected instead of "Unable to log in with provided credentials."
'User account is disabled.'
is expected, I think. In JSONWebTokenSerializer.validate
, it seems to be implemented but not working.
How to reproduce
Install django and DRF and DRF-jwt with pip, and just start new app.
$ django-admin startproject myapp
Add rest_framework
and rest_framework_jwt
into INSTALLED_APPS
in settings.py
.
Then, myapp.urls
edit as follows.
from django.contrib import admin
from django.urls import path
from rest_framework_jwt.views import ObtainJSONWebToken
urlpatterns = [
path('admin/', admin.site.urls),
path('jwt/create', ObtainJSONWebToken.as_view())
]
Execute migration, create superuser and login to admin page.
$ python manage.py makemigration
$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver
Then, create new user (for example, username
is 'user'
, password
is 'hogefuga'
) and turn off Activate
from admin page. Finally, post credentials to /jwt/create
.
$ curl -X POST -d "username=user&password=hogefuga" http://127.0.0.1:8000/jwt/create
{"non_field_errors":["Unable to log in with provided credentials."]}
In my opinion
In JSONWebTokenSerializer.validate()
, User is authenticated using settings.AUTHENTICATION_BACKENDS
('django.contrib.auth.backends.ModelBackend'
is used by default). Then, Error handling is executed based on the value which is returned by AUTHENTICATION_BACKENDS.
django.contrib.auth.backends.ModelBackend
is return user instance, when
- user which has specified username exists
and
- the user is activated (
is_active
== True)
However, if the user is not activated, it returns None
instead, now.
So, current implementation of JSONWebTokenSerializer
cannot handle 'User account is disabled.'
error.
Are there any workaround for this issue? I want to distinguish these two errors.
Problem is: AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
the default "ModelBackend" authentication backend does not allow users with is_active = False to log in.
so if you want to login with is_active= False, try AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
checkout : https://docs.djangoproject.com/en/2.0/ref/contrib/auth/#django.contrib.auth.models.User.is_active
for more detail.
Sorry, I'm confused.
I don't want to allow user with is_active = False to login. I want to know the real reason of refusing login.
This issue is a problem of django-rest-framework-jwt, I think. When user which is not activated try to login, Django refuse by default('django.contrib.auth.backends.ModelBackend'
). This is because of 'The user is not activated.', not 'Unable to log in with provided credentials.'.
Current implemention of django-rest-framework-jw try to handle this, but it contains a bug , so it return 'Unable to log in with provided credentials.'.
if all(credentials.values()): user = authenticate(**credentials)
if user:
if not user.is_active:
msg = _('User account is disabled.')
raise serializers.ValidationError(msg)
..............
in this portion ,**authenticate(credentials) will return none when is_active = false because of 'django.contrib.auth.backends.ModelBackend'
due to user = none if user: is False so it returns 'Unable to log in with provided credentials.'. i don't know if django-rest-framework-jwt is aware of it or not ...or i may also be wrong