Skip to content

Commit 9911c85

Browse files
authored
Add option to allow inactive user authentication and token generation (jazzband#834)
1 parent 79a0d52 commit 9911c85

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

rest_framework_simplejwt/authentication.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def get_user(self, validated_token: Token) -> AuthUser:
131131
except self.user_model.DoesNotExist:
132132
raise AuthenticationFailed(_("User not found"), code="user_not_found")
133133

134-
if not user.is_active:
134+
if api_settings.CHECK_USER_IS_ACTIVE and not user.is_active:
135135
raise AuthenticationFailed(_("User is inactive"), code="user_inactive")
136136

137137
if api_settings.CHECK_REVOKE_TOKEN:
@@ -175,4 +175,6 @@ def default_user_authentication_rule(user: AuthUser) -> bool:
175175
# `AllowAllUsersModelBackend`. However, we explicitly prevent inactive
176176
# users from authenticating to enforce a reasonable policy and provide
177177
# sensible backwards compatibility with older Django versions.
178-
return user is not None and user.is_active
178+
return user is not None and (
179+
not api_settings.CHECK_USER_IS_ACTIVE or user.is_active
180+
)

rest_framework_simplejwt/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
4545
"CHECK_REVOKE_TOKEN": False,
4646
"REVOKE_TOKEN_CLAIM": "hash_password",
47+
"CHECK_USER_IS_ACTIVE": True,
4748
}
4849

4950
IMPORT_STRINGS = (

tests/test_authentication.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,31 @@ def test_get_user(self):
161161
# Otherwise, should return correct user
162162
self.assertEqual(self.backend.get_user(payload).id, u.id)
163163

164+
@override_api_settings(
165+
CHECK_USER_IS_ACTIVE=False,
166+
)
167+
def test_get_inactive_user(self):
168+
payload = {"some_other_id": "foo"}
169+
170+
# Should raise error if no recognizable user identification
171+
with self.assertRaises(InvalidToken):
172+
self.backend.get_user(payload)
173+
174+
payload[api_settings.USER_ID_CLAIM] = 42
175+
176+
# Should raise exception if user not found
177+
with self.assertRaises(AuthenticationFailed):
178+
self.backend.get_user(payload)
179+
180+
u = User.objects.create_user(username="markhamill")
181+
u.is_active = False
182+
u.save()
183+
184+
payload[api_settings.USER_ID_CLAIM] = getattr(u, api_settings.USER_ID_FIELD)
185+
186+
# should return correct user
187+
self.assertEqual(self.backend.get_user(payload).id, u.id)
188+
164189
@override_api_settings(
165190
CHECK_REVOKE_TOKEN=True, REVOKE_TOKEN_CLAIM="revoke_token_claim"
166191
)

tests/test_serializers.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.conf import settings
66
from django.contrib.auth import get_user_model
77
from django.core import exceptions as django_exceptions
8-
from django.test import TestCase
8+
from django.test import TestCase, override_settings
99
from rest_framework import exceptions as drf_exceptions
1010

1111
from rest_framework_simplejwt.exceptions import TokenError
@@ -105,6 +105,29 @@ def test_it_should_raise_if_user_not_active(self):
105105
with self.assertRaises(drf_exceptions.AuthenticationFailed):
106106
s.is_valid()
107107

108+
@override_settings(
109+
AUTHENTICATION_BACKENDS=[
110+
"django.contrib.auth.backends.AllowAllUsersModelBackend",
111+
"django.contrib.auth.backends.ModelBackend",
112+
]
113+
)
114+
@override_api_settings(
115+
CHECK_USER_IS_ACTIVE=False,
116+
)
117+
def test_it_should_validate_if_user_inactive_but_rule_allows(self):
118+
self.user.is_active = False
119+
self.user.save()
120+
121+
s = TokenObtainSerializer(
122+
context=MagicMock(),
123+
data={
124+
TokenObtainSerializer.username_field: self.username,
125+
"password": self.password,
126+
},
127+
)
128+
129+
self.assertTrue(s.is_valid())
130+
108131

109132
class TestTokenObtainSlidingSerializer(TestCase):
110133
def setUp(self):

0 commit comments

Comments
 (0)