From 5aacee293a94745b710917c0822b638606b9c69c Mon Sep 17 00:00:00 2001 From: Pierre Chiquet Date: Thu, 2 May 2024 15:55:54 +0200 Subject: [PATCH 1/5] add test_for_user_fails_if_is_active_false --- tests/test_tokens.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_tokens.py b/tests/test_tokens.py index 47702e33a..97a03a75c 100644 --- a/tests/test_tokens.py +++ b/tests/test_tokens.py @@ -390,6 +390,15 @@ def test_for_user_with_username(self): token = MyToken.for_user(self.user) self.assertEqual(token[api_settings.USER_ID_CLAIM], self.username) + def test_for_user_fails_if_is_active_false(self): + # works with is_active=True + token = MyToken.for_user(self.user) + + # fails with is_active=False + self.user.is_active = False + with self.assertRaises(TokenError): + token = MyToken.for_user(self.user) + @override_api_settings(CHECK_REVOKE_TOKEN=True) def test_revoke_token_claim_included_in_authorization_token(self): token = MyToken.for_user(self.user) From fb7f45e31f81ba43be15cb2b9e6dfd6225224555 Mon Sep 17 00:00:00 2001 From: Pierre Chiquet Date: Thu, 2 May 2024 16:07:07 +0200 Subject: [PATCH 2/5] check USER_AUTHENTICATION_RULE in Token.for_user() --- rest_framework_simplejwt/tokens.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rest_framework_simplejwt/tokens.py b/rest_framework_simplejwt/tokens.py index 1eeadcf25..fe086c0c0 100644 --- a/rest_framework_simplejwt/tokens.py +++ b/rest_framework_simplejwt/tokens.py @@ -206,6 +206,11 @@ def for_user(cls: Type[T], user: AuthUser) -> T: if not isinstance(user_id, int): user_id = str(user_id) + # Prevent incorrect usage of Token.for_user (when creating tokens manually) + # see https://github.com/jazzband/djangorestframework-simplejwt/issues/779 + if not api_settings.USER_AUTHENTICATION_RULE(user): + raise TokenError(_("Token is invalid or expired")) + token = cls() token[api_settings.USER_ID_CLAIM] = user_id From f679a1a374b276c69ddc07df834ff93969ce34dc Mon Sep 17 00:00:00 2001 From: Pierre Chiquet Date: Thu, 2 May 2024 16:29:56 +0200 Subject: [PATCH 3/5] add for_validated_user to prevent double validation --- rest_framework_simplejwt/serializers.py | 2 +- rest_framework_simplejwt/tokens.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rest_framework_simplejwt/serializers.py b/rest_framework_simplejwt/serializers.py index 3dc318687..142ba6cfc 100644 --- a/rest_framework_simplejwt/serializers.py +++ b/rest_framework_simplejwt/serializers.py @@ -63,7 +63,7 @@ def validate(self, attrs: Dict[str, Any]) -> Dict[Any, Any]: @classmethod def get_token(cls, user: AuthUser) -> Token: - return cls.token_class.for_user(user) # type: ignore + return cls.token_class.for_validated_user(user) # type: ignore class TokenObtainPairSerializer(TokenObtainSerializer): diff --git a/rest_framework_simplejwt/tokens.py b/rest_framework_simplejwt/tokens.py index fe086c0c0..54e86ec82 100644 --- a/rest_framework_simplejwt/tokens.py +++ b/rest_framework_simplejwt/tokens.py @@ -202,14 +202,17 @@ def for_user(cls: Type[T], user: AuthUser) -> T: Returns an authorization token for the given user that will be provided after authenticating the user's credentials. """ - user_id = getattr(user, api_settings.USER_ID_FIELD) - if not isinstance(user_id, int): - user_id = str(user_id) - # Prevent incorrect usage of Token.for_user (when creating tokens manually) # see https://github.com/jazzband/djangorestframework-simplejwt/issues/779 if not api_settings.USER_AUTHENTICATION_RULE(user): raise TokenError(_("Token is invalid or expired")) + return cls.for_validated_user(user) + + @classmethod + def for_validated_user(cls: Type[T], user: AuthUser) -> T: + user_id = getattr(user, api_settings.USER_ID_FIELD) + if not isinstance(user_id, int): + user_id = str(user_id) token = cls() token[api_settings.USER_ID_CLAIM] = user_id @@ -283,11 +286,11 @@ def blacklist(self) -> BlacklistedToken: return BlacklistedToken.objects.get_or_create(token=token) @classmethod - def for_user(cls: Type[T], user: AuthUser) -> T: + def for_validated_user(cls: Type[T], user: AuthUser) -> T: """ Adds this token to the outstanding token list. """ - token = super().for_user(user) # type: ignore + token = super().for_validated_user(user) # type: ignore jti = token[api_settings.JTI_CLAIM] exp = token["exp"] From 4ba5a71778b293a19514d9b9d4823ccb5a917039 Mon Sep 17 00:00:00 2001 From: Pierre Chiquet Date: Wed, 26 Jun 2024 11:45:54 +0200 Subject: [PATCH 4/5] Add docstring for "for_validated_user" Co-authored-by: Nils Van Zuijlen --- rest_framework_simplejwt/tokens.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rest_framework_simplejwt/tokens.py b/rest_framework_simplejwt/tokens.py index 54e86ec82..5177e23d3 100644 --- a/rest_framework_simplejwt/tokens.py +++ b/rest_framework_simplejwt/tokens.py @@ -210,6 +210,12 @@ def for_user(cls: Type[T], user: AuthUser) -> T: @classmethod def for_validated_user(cls: Type[T], user: AuthUser) -> T: + """ + Returns an authorization token for the given user. + + This DOES NOT check if the provided user validates the ``USER_AUTHENTICATION_RULE``. + Use :meth:`for_user` to have the user validated. + """ user_id = getattr(user, api_settings.USER_ID_FIELD) if not isinstance(user_id, int): user_id = str(user_id) From eeb64cc4a8d09c9b821f9b4f2d62729a46ad9d6f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:46:21 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- rest_framework_simplejwt/tokens.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework_simplejwt/tokens.py b/rest_framework_simplejwt/tokens.py index 5177e23d3..98b11d427 100644 --- a/rest_framework_simplejwt/tokens.py +++ b/rest_framework_simplejwt/tokens.py @@ -212,7 +212,7 @@ def for_user(cls: Type[T], user: AuthUser) -> T: def for_validated_user(cls: Type[T], user: AuthUser) -> T: """ Returns an authorization token for the given user. - + This DOES NOT check if the provided user validates the ``USER_AUTHENTICATION_RULE``. Use :meth:`for_user` to have the user validated. """