From 9bbc4a3014df5d1da3771bbd1c7170a976f66842 Mon Sep 17 00:00:00 2001 From: alichass Date: Wed, 11 Mar 2020 06:01:03 -0400 Subject: [PATCH 01/12] change standard auth stuff to work with simplejwt --- dj_rest_auth/serializers.py | 3 ++- dj_rest_auth/utils.py | 12 +++++------- dj_rest_auth/views.py | 18 +++--------------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/dj_rest_auth/serializers.py b/dj_rest_auth/serializers.py index 07783da..b34c9cc 100644 --- a/dj_rest_auth/serializers.py +++ b/dj_rest_auth/serializers.py @@ -135,7 +135,8 @@ class JWTSerializer(serializers.Serializer): """ Serializer for JWT authentication. """ - token = serializers.CharField() + access_token = serializers.CharField() + refresh_token = serializers.CharField() user = serializers.SerializerMethodField() def get_user(self, obj): diff --git a/dj_rest_auth/utils.py b/dj_rest_auth/utils.py index b4858c8..165963d 100644 --- a/dj_rest_auth/utils.py +++ b/dj_rest_auth/utils.py @@ -17,12 +17,10 @@ def default_create_token(token_model, user, serializer): def jwt_encode(user): try: - from rest_framework_jwt.settings import api_settings + from rest_framework_simplejwt.serializers import TokenObtainPairSerializer + from rest_framework_simplejwt.views import TokenObtainPairView except ImportError: - raise ImportError("djangorestframework_jwt needs to be installed") + raise ImportError("rest-framework-simplejwt needs to be installed") - jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER - jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER - - payload = jwt_payload_handler(user) - return jwt_encode_handler(payload) + refresh = TokenObtainPairSerializer.get_token(user) + return refresh.access_token, refresh diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index c5bcb1a..f30980c 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -59,7 +59,7 @@ class LoginView(GenericAPIView): self.user = self.serializer.validated_data['user'] if getattr(settings, 'REST_USE_JWT', False): - self.token = jwt_encode(self.user) + self.access_token, self.refresh_token = jwt_encode(self.user) else: self.token = create_token(self.token_model, self.user, self.serializer) @@ -73,7 +73,8 @@ class LoginView(GenericAPIView): if getattr(settings, 'REST_USE_JWT', False): data = { 'user': self.user, - 'token': self.token + 'access_token': self.access_token, + 'refresh_token': self.refresh_token } serializer = serializer_class(instance=data, context={'request': self.request}) @@ -82,15 +83,6 @@ class LoginView(GenericAPIView): context={'request': self.request}) response = Response(serializer.data, status=status.HTTP_200_OK) - if getattr(settings, 'REST_USE_JWT', False): - from rest_framework_jwt.settings import api_settings as jwt_settings - if jwt_settings.JWT_AUTH_COOKIE: - from datetime import datetime - expiration = (datetime.utcnow() + jwt_settings.JWT_EXPIRATION_DELTA) - response.set_cookie(jwt_settings.JWT_AUTH_COOKIE, - self.token, - expires=expiration, - httponly=True) return response def post(self, request, *args, **kwargs): @@ -133,10 +125,6 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) - if getattr(settings, 'REST_USE_JWT', False): - from rest_framework_jwt.settings import api_settings as jwt_settings - if jwt_settings.JWT_AUTH_COOKIE: - response.delete_cookie(jwt_settings.JWT_AUTH_COOKIE) return response From 4fb746e6455bdb78ca1421f53c2d4c86006fbcd3 Mon Sep 17 00:00:00 2001 From: alichass Date: Wed, 11 Mar 2020 06:03:59 -0400 Subject: [PATCH 02/12] updated tests --- dj_rest_auth/tests/settings.py | 4 +--- dj_rest_auth/tests/test_api.py | 8 ++++---- dj_rest_auth/tests/test_social.py | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/dj_rest_auth/tests/settings.py b/dj_rest_auth/tests/settings.py index d18f41e..7279c21 100644 --- a/dj_rest_auth/tests/settings.py +++ b/dj_rest_auth/tests/settings.py @@ -68,7 +68,7 @@ TEMPLATES = [ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', - 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', + 'rest_framework_simplejwt.authentication.JWTAuthentication', ) } @@ -94,8 +94,6 @@ INSTALLED_APPS = [ 'dj_rest_auth', 'dj_rest_auth.registration', - - 'rest_framework_jwt' ] SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd" diff --git a/dj_rest_auth/tests/test_api.py b/dj_rest_auth/tests/test_api.py index 585b38d..5d06fac 100644 --- a/dj_rest_auth/tests/test_api.py +++ b/dj_rest_auth/tests/test_api.py @@ -152,8 +152,8 @@ class APIBasicTests(TestsMixin, TestCase): get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) - self.assertEqual('token' in self.response.json.keys(), True) - self.token = self.response.json['token'] + self.assertEqual('access_token' in self.response.json.keys(), True) + self.token = self.response.json['access_token'] def test_login_by_email(self): # starting test without allauth app @@ -382,7 +382,7 @@ class APIBasicTests(TestsMixin, TestCase): "password": self.PASS } self.post(self.login_url, data=payload, status_code=200) - self.token = self.response.json['token'] + self.token = self.response.json['access_token'] self.get(self.user_url, status_code=200) self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) @@ -426,7 +426,7 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.register_url, data={}, status_code=400) result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) - self.assertIn('token', result.data) + self.assertIn('access_token', result.data) self.assertEqual(get_user_model().objects.all().count(), user_count + 1) self._login() diff --git a/dj_rest_auth/tests/test_social.py b/dj_rest_auth/tests/test_social.py index 47c8d93..cceba3f 100644 --- a/dj_rest_auth/tests/test_social.py +++ b/dj_rest_auth/tests/test_social.py @@ -305,7 +305,7 @@ class TestSocialAuth(TestsMixin, TestCase): } self.post(self.fb_login_url, data=payload, status_code=200) - self.assertIn('token', self.response.json.keys()) + self.assertIn('access_token', self.response.json.keys()) self.assertIn('user', self.response.json.keys()) self.assertEqual(get_user_model().objects.all().count(), users_count + 1) From 23e88f9838f421b6f85471b51d9fafcf1fc7c7c0 Mon Sep 17 00:00:00 2001 From: alichass Date: Wed, 11 Mar 2020 06:05:11 -0400 Subject: [PATCH 03/12] update registration views to work with simplejwt --- dj_rest_auth/registration/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dj_rest_auth/registration/views.py b/dj_rest_auth/registration/views.py index 81ed1ea..e9577a1 100644 --- a/dj_rest_auth/registration/views.py +++ b/dj_rest_auth/registration/views.py @@ -49,7 +49,8 @@ class RegisterView(CreateAPIView): if getattr(settings, 'REST_USE_JWT', False): data = { 'user': user, - 'token': self.token + 'access_token': self.access_token, + 'refresh_token': self.refresh_token } return JWTSerializer(data).data else: @@ -68,7 +69,7 @@ class RegisterView(CreateAPIView): def perform_create(self, serializer): user = serializer.save(self.request) if getattr(settings, 'REST_USE_JWT', False): - self.token = jwt_encode(user) + self.access_token, self.refresh_token = jwt_encode(user) else: create_token(self.token_model, user, serializer) From 231ccc04a38eb1fc4a21545f75d6b0c4c70760b6 Mon Sep 17 00:00:00 2001 From: alichass Date: Wed, 11 Mar 2020 06:15:32 -0400 Subject: [PATCH 04/12] updated docs --- docs/configuration.rst | 2 +- docs/installation.rst | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index b988a76..af858c0 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -46,7 +46,7 @@ Configuration - **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True) -- **REST_USE_JWT** - Enable JWT Authentication instead of Token/Session based. This is built on top of django-rest-framework-jwt http://getblimp.github.io/django-rest-framework-jwt/, which must also be installed. (default: False) +- **REST_USE_JWT** - Enable JWT Authentication instead of Token/Session based. This is built on top of djangorestframework-simplejwt https://github.com/SimpleJWT/django-rest-framework-simplejwt, which must also be installed. (default: False) - **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False) diff --git a/docs/installation.rst b/docs/installation.rst index cba551e..5004752 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -248,12 +248,23 @@ JSON Web Token (JWT) Support (optional) By default ``dj-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, follow these steps: -1. Install `djangorestframework-jwt `_ - - ``djangorestframework-jwt`` is currently the only supported JWT library. -2. The ``JWT_PAYLOAD_HANDLER`` and ``JWT_ENCODE_HANDLER`` settings are imported from the ``django-rest-framework-jwt`` settings object. - - Refer to `the library's documentation `_ for information on using different encoders. +1. Install `djangorestframework-simplejwt `_ + - ``djangorestframework-simplejwt`` is currently the only supported JWT library. -3. Add the following configuration value to your settings file to enable JWT authentication. +2. Add a simple_jwt auth configuration to the list of authentication classes. + +.. code-block:: python + + REST_FRAMEWORK = { + ... + 'DEFAULT_AUTHENTICATION_CLASSES': ( + ... + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ) + ... + } + +3. Add the following configuration value to your settings file to enable JWT authentication in dj-rest-auth. .. code-block:: python From 6dd2aea624482cc527b0e10391a56a2dd95d3423 Mon Sep 17 00:00:00 2001 From: alichass Date: Wed, 11 Mar 2020 06:22:52 -0400 Subject: [PATCH 05/12] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 94aad46..8872a56 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setup( tests_require=[ 'responses>=0.5.0', 'django-allauth>=0.25.0', - 'djangorestframework-jwt>=1.9.0', + 'djangorestframework-simplejwt>=4.4.0 ', 'coveralls>=1.11.1' ], test_suite='runtests.runtests', From 12e79aa33ebf835579492a1dd9db970aaef4fbdc Mon Sep 17 00:00:00 2001 From: alichass Date: Thu, 19 Mar 2020 14:37:35 -0400 Subject: [PATCH 06/12] changed for use w/ cookies --- dj_rest_auth/utils.py | 1 - dj_rest_auth/views.py | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dj_rest_auth/utils.py b/dj_rest_auth/utils.py index 165963d..85912a6 100644 --- a/dj_rest_auth/utils.py +++ b/dj_rest_auth/utils.py @@ -18,7 +18,6 @@ def default_create_token(token_model, user, serializer): def jwt_encode(user): try: from rest_framework_simplejwt.serializers import TokenObtainPairSerializer - from rest_framework_simplejwt.views import TokenObtainPairView except ImportError: raise ImportError("rest-framework-simplejwt needs to be installed") diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index f30980c..37e4880 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -83,6 +83,15 @@ class LoginView(GenericAPIView): context={'request': self.request}) response = Response(serializer.data, status=status.HTTP_200_OK) + if getattr(settings, 'REST_USE_JWT', False): + from rest_framework_simplejwt.settings import api_settings as jwt_settings + #if jwt_settings.JWT_AUTH_COOKIE #this needs to be added to simplejwt + from datetime import datetime + expiration = (datetime.utcnow() + jwt_settings.ACCESS_TOKEN_LIFETIME) + response.set_cookie('somestring', #replace with jwt_settings.JWT_AUTH_COOKIE + self.access_token, + expires=expiration, + httponly=True) return response def post(self, request, *args, **kwargs): @@ -125,6 +134,10 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) + if getattr(settings, 'REST_USE_JWT', False): + from rest_framework_simplejwt.settings import api_settings as jwt_settings + #if jwt_settings.JWT_AUTH_COOKIE #this needs to be added to simplejwt + response.delete_cookie('somestring') #replace with jwt_settings.JWT_AUTH_COOKIE return response From f73f3af1d3698336d0ad15dceb0add289cb2188c Mon Sep 17 00:00:00 2001 From: alichass Date: Thu, 19 Mar 2020 17:09:20 -0400 Subject: [PATCH 07/12] hopefully this should work? --- dj_rest_auth/app_settings.py | 3 +++ dj_rest_auth/tests/settings.py | 2 +- dj_rest_auth/utils.py | 30 +++++++++++++++++++++++++++++- dj_rest_auth/views.py | 22 +++++++++++----------- docs/installation.rst | 2 +- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/dj_rest_auth/app_settings.py b/dj_rest_auth/app_settings.py index d89202d..39e44ef 100644 --- a/dj_rest_auth/app_settings.py +++ b/dj_rest_auth/app_settings.py @@ -34,3 +34,6 @@ PasswordResetConfirmSerializer = serializers.get( ) PasswordChangeSerializer = import_callable(serializers.get('PASSWORD_CHANGE_SERIALIZER', DefaultPasswordChangeSerializer)) + +JWT_AUTH_COOKIE = getattr(settings, 'JWT_AUTH_COOKIE', None) + diff --git a/dj_rest_auth/tests/settings.py b/dj_rest_auth/tests/settings.py index 7279c21..3d02309 100644 --- a/dj_rest_auth/tests/settings.py +++ b/dj_rest_auth/tests/settings.py @@ -68,7 +68,7 @@ TEMPLATES = [ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', - 'rest_framework_simplejwt.authentication.JWTAuthentication', + 'dj_rest_auth.utils.JWTCookieAuthentication', ) } diff --git a/dj_rest_auth/utils.py b/dj_rest_auth/utils.py index 85912a6..2b8094a 100644 --- a/dj_rest_auth/utils.py +++ b/dj_rest_auth/utils.py @@ -1,5 +1,5 @@ from importlib import import_module - +from .app_settings import JWT_AUTH_COOKIE def import_callable(path_or_callable): if hasattr(path_or_callable, '__call__'): @@ -23,3 +23,31 @@ def jwt_encode(user): refresh = TokenObtainPairSerializer.get_token(user) return refresh.access_token, refresh + + +try: + from rest_framework_simplejwt.authentication import JWTAuthentication + + class JWTCookieAuthentication(JWTAuthentication): + """ + An authentication plugin that hopefully authenticates requests through a JSON web + token provided in a request cookie (and through the header as normal, with a preference to the header). + """ + def authenticate(self, request): + header = self.get_header(request) + if header is None: + if JWT_AUTH_COOKIE: # or settings.JWT_AUTH_COOKIE + raw_token = request.COOKIES.get(JWT_AUTH_COOKIE) # or settings.jwt_auth_cookie + else: + return None + else: + raw_token = self.get_raw_token(header) + + if raw_token is None: + return None + + validated_token = self.get_validated_token(raw_token) + + return self.get_user(validated_token), validated_token +except ImportError as I: + pass diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index 37e4880..2896209 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -16,7 +16,7 @@ from .app_settings import (JWTSerializer, LoginSerializer, PasswordChangeSerializer, PasswordResetConfirmSerializer, PasswordResetSerializer, TokenSerializer, - UserDetailsSerializer, create_token) + UserDetailsSerializer, create_token, JWT_AUTH_COOKIE) from .models import TokenModel from .utils import jwt_encode @@ -85,13 +85,13 @@ class LoginView(GenericAPIView): response = Response(serializer.data, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): from rest_framework_simplejwt.settings import api_settings as jwt_settings - #if jwt_settings.JWT_AUTH_COOKIE #this needs to be added to simplejwt - from datetime import datetime - expiration = (datetime.utcnow() + jwt_settings.ACCESS_TOKEN_LIFETIME) - response.set_cookie('somestring', #replace with jwt_settings.JWT_AUTH_COOKIE - self.access_token, - expires=expiration, - httponly=True) + if JWT_AUTH_COOKIE: #this needs to be added to simplejwt + from datetime import datetime + expiration = (datetime.utcnow() + jwt_settings.ACCESS_TOKEN_LIFETIME) + response.set_cookie(JWT_AUTH_COOKIE, #this needs to be added to simplejwt + self.access_token, + expires=expiration, + httponly=True) return response def post(self, request, *args, **kwargs): @@ -135,9 +135,9 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): - from rest_framework_simplejwt.settings import api_settings as jwt_settings - #if jwt_settings.JWT_AUTH_COOKIE #this needs to be added to simplejwt - response.delete_cookie('somestring') #replace with jwt_settings.JWT_AUTH_COOKIE + # from rest_framework_simplejwt.settings import api_settings as jwt_settings + if JWT_AUTH_COOKIE: #this needs to be added to simplejwt + response.delete_cookie(JWT_AUTH_COOKIE) #this needs to be added to simplejwt return response diff --git a/docs/installation.rst b/docs/installation.rst index 5004752..fa4345d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -259,7 +259,7 @@ By default ``dj-rest-auth`` uses Django's Token-based authentication. If you wan ... 'DEFAULT_AUTHENTICATION_CLASSES': ( ... - 'rest_framework_simplejwt.authentication.JWTAuthentication', + 'dj_rest_auth.utils.JWTCookieAuthentication', ) ... } From 475e0b94c2cb00e9c260cd944e91d850627fd211 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Mar 2020 05:41:16 -0500 Subject: [PATCH 08/12] Support for Http-Only JWT Cookies --- demo/requirements.pip | 1 + dev-requirements.txt | 2 +- dj_rest_auth/tests/requirements.pip | 3 +- dj_rest_auth/tests/test_api.py | 299 +++------------------------- dj_rest_auth/tests/test_social.py | 3 - dj_rest_auth/tests/urls.py | 12 ++ dj_rest_auth/utils.py | 15 +- dj_rest_auth/views.py | 21 +- docs/configuration.rst | 2 +- 9 files changed, 64 insertions(+), 294 deletions(-) diff --git a/demo/requirements.pip b/demo/requirements.pip index 7442229..6d24967 100644 --- a/demo/requirements.pip +++ b/demo/requirements.pip @@ -1,5 +1,6 @@ django>=1.9.0 dj-rest-auth==0.1.4 djangorestframework>=3.7.0 +djangorestframework-simplejwt==4.4.0 django-allauth>=0.24.1 django-rest-swagger==2.0.7 diff --git a/dev-requirements.txt b/dev-requirements.txt index 9d2af20..cee8ea6 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,5 @@ --editable . responses>=0.5.0 -djangorestframework-jwt +djangorestframework-simplejwt==4.4.0 django-allauth coveralls>=1.11.1 \ No newline at end of file diff --git a/dj_rest_auth/tests/requirements.pip b/dj_rest_auth/tests/requirements.pip index f48ee3c..4ff9b2d 100644 --- a/dj_rest_auth/tests/requirements.pip +++ b/dj_rest_auth/tests/requirements.pip @@ -1,5 +1,6 @@ django-allauth>=0.25.0 responses>=0.3.0 flake8==2.4.0 -djangorestframework-jwt>=1.7.2 +Django==3.0.4 +djangorestframework-simplejwt==4.4.0 djangorestframework>=3.6.4 diff --git a/dj_rest_auth/tests/test_api.py b/dj_rest_auth/tests/test_api.py index 5d06fac..4519186 100644 --- a/dj_rest_auth/tests/test_api.py +++ b/dj_rest_auth/tests/test_api.py @@ -144,293 +144,46 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.login_url, data=payload, status_code=200) @override_settings(REST_USE_JWT=True) - def test_login_jwt(self): + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_login_jwt_sets_cookie(self): payload = { "username": self.USERNAME, "password": self.PASS } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - - self.post(self.login_url, data=payload, status_code=200) - self.assertEqual('access_token' in self.response.json.keys(), True) - self.token = self.response.json['access_token'] - - def test_login_by_email(self): - # starting test without allauth app - settings.INSTALLED_APPS.remove('allauth') - - payload = { - "email": self.EMAIL.lower(), - "password": self.PASS - } - # there is no users in db so it should throw error (400) - self.post(self.login_url, data=payload, status_code=400) - - self.post(self.password_change_url, status_code=403) - - # create user - user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) - - # test auth by email - self.post(self.login_url, data=payload, status_code=200) - self.assertEqual('key' in self.response.json.keys(), True) - self.token = self.response.json['key'] - - # test auth by email in different case - payload = { - "email": self.EMAIL.upper(), - "password": self.PASS - } - self.post(self.login_url, data=payload, status_code=200) - self.assertEqual('key' in self.response.json.keys(), True) - self.token = self.response.json['key'] - - # test inactive user - user.is_active = False - user.save() - self.post(self.login_url, data=payload, status_code=400) - - # test wrong email/password - payload = { - "email": 't' + self.EMAIL, - "password": self.PASS - } - self.post(self.login_url, data=payload, status_code=400) - - # test empty payload - self.post(self.login_url, data={}, status_code=400) - - # bring back allauth - settings.INSTALLED_APPS.append('allauth') - - def test_password_change(self): - login_payload = { - "username": self.USERNAME, - "password": self.PASS - } - get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - self.post(self.login_url, data=login_payload, status_code=200) - self.token = self.response.json['key'] - - new_password_payload = { - "new_password1": "new_person", - "new_password2": "new_person" - } - self.post( - self.password_change_url, - data=new_password_payload, - status_code=200 - ) - - # user should not be able to login using old password - self.post(self.login_url, data=login_payload, status_code=400) - - # new password should work - login_payload['password'] = new_password_payload['new_password1'] - self.post(self.login_url, data=login_payload, status_code=200) - - # pass1 and pass2 are not equal - new_password_payload = { - "new_password1": "new_person1", - "new_password2": "new_person" - } - self.post( - self.password_change_url, - data=new_password_payload, - status_code=400 - ) - - # send empty payload - self.post(self.password_change_url, data={}, status_code=400) - - @override_settings(OLD_PASSWORD_FIELD_ENABLED=True) - def test_password_change_with_old_password(self): - login_payload = { - "username": self.USERNAME, - "password": self.PASS - } - get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - self.post(self.login_url, data=login_payload, status_code=200) - self.token = self.response.json['key'] - - new_password_payload = { - "old_password": "%s!" % self.PASS, # wrong password - "new_password1": "new_person", - "new_password2": "new_person" - } - self.post( - self.password_change_url, - data=new_password_payload, - status_code=400 - ) - - new_password_payload = { - "old_password": self.PASS, - "new_password1": "new_person", - "new_password2": "new_person" - } - self.post( - self.password_change_url, - data=new_password_payload, - status_code=200 - ) - - # user should not be able to login using old password - self.post(self.login_url, data=login_payload, status_code=400) - - # new password should work - login_payload['password'] = new_password_payload['new_password1'] - self.post(self.login_url, data=login_payload, status_code=200) - - def test_password_reset(self): - user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) - - # call password reset - mail_count = len(mail.outbox) - payload = {'email': self.EMAIL} - self.post(self.password_reset_url, data=payload, status_code=200) - self.assertEqual(len(mail.outbox), mail_count + 1) - - url_kwargs = self._generate_uid_and_token(user) - url = reverse('rest_password_reset_confirm') - - # wrong token - data = { - 'new_password1': self.NEW_PASS, - 'new_password2': self.NEW_PASS, - 'uid': force_text(url_kwargs['uid']), - 'token': '-wrong-token-' - } - self.post(url, data=data, status_code=400) - - # wrong uid - data = { - 'new_password1': self.NEW_PASS, - 'new_password2': self.NEW_PASS, - 'uid': '-wrong-uid-', - 'token': url_kwargs['token'] - } - self.post(url, data=data, status_code=400) - - # wrong token and uid - data = { - 'new_password1': self.NEW_PASS, - 'new_password2': self.NEW_PASS, - 'uid': '-wrong-uid-', - 'token': '-wrong-token-' - } - self.post(url, data=data, status_code=400) - - # valid payload - data = { - 'new_password1': self.NEW_PASS, - 'new_password2': self.NEW_PASS, - 'uid': force_text(url_kwargs['uid']), - 'token': url_kwargs['token'] - } - url = reverse('rest_password_reset_confirm') - self.post(url, data=data, status_code=200) - - payload = { - "username": self.USERNAME, - "password": self.NEW_PASS - } - self.post(self.login_url, data=payload, status_code=200) - - def test_password_reset_with_email_in_different_case(self): - get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS) - - # call password reset in upper case - mail_count = len(mail.outbox) - payload = {'email': self.EMAIL.upper()} - self.post(self.password_reset_url, data=payload, status_code=200) - self.assertEqual(len(mail.outbox), mail_count + 1) - - def test_password_reset_with_invalid_email(self): - """ - Invalid email should not raise error, as this would leak users - """ - get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) - - # call password reset - mail_count = len(mail.outbox) - payload = {'email': 'nonexisting@email.com'} - self.post(self.password_reset_url, data=payload, status_code=200) - self.assertEqual(len(mail.outbox), mail_count) - - def test_user_details(self): - user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) - payload = { - "username": self.USERNAME, - "password": self.PASS - } - self.post(self.login_url, data=payload, status_code=200) - self.token = self.response.json['key'] - self.get(self.user_url, status_code=200) - - self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) - user = get_user_model().objects.get(pk=user.pk) - self.assertEqual(user.first_name, self.response.json['first_name']) - self.assertEqual(user.last_name, self.response.json['last_name']) - self.assertEqual(user.email, self.response.json['email']) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertTrue('jwt-auth' in resp.cookies.keys()) @override_settings(REST_USE_JWT=True) - def test_user_details_using_jwt(self): - user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_logout_jwt_deletes_cookie(self): payload = { "username": self.USERNAME, "password": self.PASS } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) - self.token = self.response.json['access_token'] - self.get(self.user_url, status_code=200) - - self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) - user = get_user_model().objects.get(pk=user.pk) - self.assertEqual(user.email, self.response.json['email']) - - def test_registration(self): - user_count = get_user_model().objects.all().count() - - # test empty payload - self.post(self.register_url, data={}, status_code=400) - - result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) - self.assertIn('key', result.data) - self.assertEqual(get_user_model().objects.all().count(), user_count + 1) - - new_user = get_user_model().objects.latest('id') - self.assertEqual(new_user.username, self.REGISTRATION_DATA['username']) - - self._login() - self._logout() - - @override_settings(REST_AUTH_REGISTER_PERMISSION_CLASSES=(CustomPermissionClass,)) - def test_registration_with_custom_permission_class(self): - - class CustomRegisterView(RegisterView): - permission_classes = register_permission_classes() - authentication_classes = () - - factory = APIRequestFactory() - request = factory.post('/customer/details', self.REGISTRATION_DATA, format='json') - - response = CustomRegisterView.as_view()(request) - self.assertEqual(response.data['detail'], CustomPermissionClass.message) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + resp = self.post(self.logout_url, status=200) + self.assertEqual('', resp.cookies.get('jwt-auth').value) @override_settings(REST_USE_JWT=True) - def test_registration_with_jwt(self): - user_count = get_user_model().objects.all().count() - - self.post(self.register_url, data={}, status_code=400) - - result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) - self.assertIn('access_token', result.data) - self.assertEqual(get_user_model().objects.all().count(), user_count + 1) - - self._login() - self._logout() + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + @override_settings(REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.utils.JWTCookieAuthentication' + ] + )) + @override_settings(REST_SESSION_LOGIN=False) + def test_cookie_authentication(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertEqual(['jwt-auth'], list(resp.cookies.keys())) + resp = self.get('/protected-view/') + self.assertEquals(resp.status_code, 200) def test_registration_with_invalid_password(self): data = self.REGISTRATION_DATA.copy() diff --git a/dj_rest_auth/tests/test_social.py b/dj_rest_auth/tests/test_social.py index cceba3f..819b153 100644 --- a/dj_rest_auth/tests/test_social.py +++ b/dj_rest_auth/tests/test_social.py @@ -17,9 +17,6 @@ except ImportError: from django.core.urlresolvers import reverse - - - @override_settings(ROOT_URLCONF="tests.urls") class TestSocialAuth(TestsMixin, TestCase): diff --git a/dj_rest_auth/tests/urls.py b/dj_rest_auth/tests/urls.py index be199a5..7579e83 100644 --- a/dj_rest_auth/tests/urls.py +++ b/dj_rest_auth/tests/urls.py @@ -11,10 +11,21 @@ from dj_rest_auth.urls import urlpatterns from django.conf.urls import include, url from django.views.generic import TemplateView from rest_framework.decorators import api_view +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import permissions from . import django_urls +class ExampleProtectedView(APIView): + permission_classes = [permissions.IsAuthenticated] + + + def get(self, *args, **kwargs): + return Response(dict(success=True)) + + class FacebookLogin(SocialLoginView): adapter_class = FacebookOAuth2Adapter @@ -64,6 +75,7 @@ urlpatterns += [ url(r'^social-login/facebook/connect/$', FacebookConnect.as_view(), name='fb_connect'), url(r'^social-login/twitter/connect/$', TwitterConnect.as_view(), name='tw_connect'), url(r'^socialaccounts/$', SocialAccountListView.as_view(), name='social_account_list'), + url(r'^protected-view/$', ExampleProtectedView.as_view()), url(r'^socialaccounts/(?P\d+)/disconnect/$', SocialAccountDisconnectView.as_view(), name='social_account_disconnect'), url(r'^accounts/', include('allauth.socialaccount.urls')) diff --git a/dj_rest_auth/utils.py b/dj_rest_auth/utils.py index 2b8094a..7cbf3ad 100644 --- a/dj_rest_auth/utils.py +++ b/dj_rest_auth/utils.py @@ -1,5 +1,5 @@ from importlib import import_module -from .app_settings import JWT_AUTH_COOKIE + def import_callable(path_or_callable): if hasattr(path_or_callable, '__call__'): @@ -31,13 +31,16 @@ try: class JWTCookieAuthentication(JWTAuthentication): """ An authentication plugin that hopefully authenticates requests through a JSON web - token provided in a request cookie (and through the header as normal, with a preference to the header). + token provided in a request cookie (and through the header as normal, with a + preference to the header). """ def authenticate(self, request): + from django.conf import settings + cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) header = self.get_header(request) if header is None: - if JWT_AUTH_COOKIE: # or settings.JWT_AUTH_COOKIE - raw_token = request.COOKIES.get(JWT_AUTH_COOKIE) # or settings.jwt_auth_cookie + if cookie_name: + raw_token = request.COOKIES.get(cookie_name) else: return None else: @@ -47,7 +50,7 @@ try: return None validated_token = self.get_validated_token(raw_token) - return self.get_user(validated_token), validated_token -except ImportError as I: + +except ImportError: pass diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index 2896209..a31e879 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -16,7 +16,7 @@ from .app_settings import (JWTSerializer, LoginSerializer, PasswordChangeSerializer, PasswordResetConfirmSerializer, PasswordResetSerializer, TokenSerializer, - UserDetailsSerializer, create_token, JWT_AUTH_COOKIE) + UserDetailsSerializer, create_token) from .models import TokenModel from .utils import jwt_encode @@ -84,14 +84,17 @@ class LoginView(GenericAPIView): response = Response(serializer.data, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): + cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) from rest_framework_simplejwt.settings import api_settings as jwt_settings - if JWT_AUTH_COOKIE: #this needs to be added to simplejwt + if cookie_name: from datetime import datetime expiration = (datetime.utcnow() + jwt_settings.ACCESS_TOKEN_LIFETIME) - response.set_cookie(JWT_AUTH_COOKIE, #this needs to be added to simplejwt - self.access_token, - expires=expiration, - httponly=True) + response.set_cookie( + cookie_name, + self.access_token, + expires=expiration, + httponly=True + ) return response def post(self, request, *args, **kwargs): @@ -135,9 +138,9 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): - # from rest_framework_simplejwt.settings import api_settings as jwt_settings - if JWT_AUTH_COOKIE: #this needs to be added to simplejwt - response.delete_cookie(JWT_AUTH_COOKIE) #this needs to be added to simplejwt + cookie_name = getattr(settings, 'JWT_AUTH_COOKIE') + if cookie_name: + response.delete_cookie(cookie_name) return response diff --git a/docs/configuration.rst b/docs/configuration.rst index af858c0..d4d67cc 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -47,7 +47,7 @@ Configuration - **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True) - **REST_USE_JWT** - Enable JWT Authentication instead of Token/Session based. This is built on top of djangorestframework-simplejwt https://github.com/SimpleJWT/django-rest-framework-simplejwt, which must also be installed. (default: False) - +- **JWT_AUTH_COOKIE** - The cookie name/key. - **OLD_PASSWORD_FIELD_ENABLED** - set it to True if you want to have old password verification on password change enpoint (default: False) - **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change From ce3b90dea6e2bd5b691d6f2c84fd9e85b07a5952 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Mar 2020 05:52:26 -0500 Subject: [PATCH 09/12] Re-adds previous tests --- dj_rest_auth/tests/test_api.py | 345 ++++++++++++++++++++++++++++++--- dj_rest_auth/views.py | 2 +- 2 files changed, 319 insertions(+), 28 deletions(-) diff --git a/dj_rest_auth/tests/test_api.py b/dj_rest_auth/tests/test_api.py index 4519186..f0607b5 100644 --- a/dj_rest_auth/tests/test_api.py +++ b/dj_rest_auth/tests/test_api.py @@ -144,46 +144,293 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.login_url, data=payload, status_code=200) @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - def test_login_jwt_sets_cookie(self): + def test_login_jwt(self): payload = { "username": self.USERNAME, "password": self.PASS } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - resp = self.post(self.login_url, data=payload, status_code=200) - self.assertTrue('jwt-auth' in resp.cookies.keys()) - @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - def test_logout_jwt_deletes_cookie(self): - payload = { - "username": self.USERNAME, - "password": self.PASS - } - get_user_model().objects.create_user(self.USERNAME, '', self.PASS) self.post(self.login_url, data=payload, status_code=200) - resp = self.post(self.logout_url, status=200) - self.assertEqual('', resp.cookies.get('jwt-auth').value) + self.assertEqual('access_token' in self.response.json.keys(), True) + self.token = self.response.json['access_token'] + + def test_login_by_email(self): + # starting test without allauth app + settings.INSTALLED_APPS.remove('allauth') - @override_settings(REST_USE_JWT=True) - @override_settings(JWT_AUTH_COOKIE='jwt-auth') - @override_settings(REST_FRAMEWORK=dict( - DEFAULT_AUTHENTICATION_CLASSES=[ - 'dj_rest_auth.utils.JWTCookieAuthentication' - ] - )) - @override_settings(REST_SESSION_LOGIN=False) - def test_cookie_authentication(self): payload = { + "email": self.EMAIL.lower(), + "password": self.PASS + } + # there is no users in db so it should throw error (400) + self.post(self.login_url, data=payload, status_code=400) + + self.post(self.password_change_url, status_code=403) + + # create user + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # test auth by email + self.post(self.login_url, data=payload, status_code=200) + self.assertEqual('key' in self.response.json.keys(), True) + self.token = self.response.json['key'] + + # test auth by email in different case + payload = { + "email": self.EMAIL.upper(), + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.assertEqual('key' in self.response.json.keys(), True) + self.token = self.response.json['key'] + + # test inactive user + user.is_active = False + user.save() + self.post(self.login_url, data=payload, status_code=400) + + # test wrong email/password + payload = { + "email": 't' + self.EMAIL, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=400) + + # test empty payload + self.post(self.login_url, data={}, status_code=400) + + # bring back allauth + settings.INSTALLED_APPS.append('allauth') + + def test_password_change(self): + login_payload = { "username": self.USERNAME, "password": self.PASS } get_user_model().objects.create_user(self.USERNAME, '', self.PASS) - resp = self.post(self.login_url, data=payload, status_code=200) - self.assertEqual(['jwt-auth'], list(resp.cookies.keys())) - resp = self.get('/protected-view/') - self.assertEquals(resp.status_code, 200) + self.post(self.login_url, data=login_payload, status_code=200) + self.token = self.response.json['key'] + + new_password_payload = { + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=200 + ) + + # user should not be able to login using old password + self.post(self.login_url, data=login_payload, status_code=400) + + # new password should work + login_payload['password'] = new_password_payload['new_password1'] + self.post(self.login_url, data=login_payload, status_code=200) + + # pass1 and pass2 are not equal + new_password_payload = { + "new_password1": "new_person1", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=400 + ) + + # send empty payload + self.post(self.password_change_url, data={}, status_code=400) + + @override_settings(OLD_PASSWORD_FIELD_ENABLED=True) + def test_password_change_with_old_password(self): + login_payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + self.post(self.login_url, data=login_payload, status_code=200) + self.token = self.response.json['key'] + + new_password_payload = { + "old_password": "%s!" % self.PASS, # wrong password + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=400 + ) + + new_password_payload = { + "old_password": self.PASS, + "new_password1": "new_person", + "new_password2": "new_person" + } + self.post( + self.password_change_url, + data=new_password_payload, + status_code=200 + ) + + # user should not be able to login using old password + self.post(self.login_url, data=login_payload, status_code=400) + + # new password should work + login_payload['password'] = new_password_payload['new_password1'] + self.post(self.login_url, data=login_payload, status_code=200) + + def test_password_reset(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # call password reset + mail_count = len(mail.outbox) + payload = {'email': self.EMAIL} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count + 1) + + url_kwargs = self._generate_uid_and_token(user) + url = reverse('rest_password_reset_confirm') + + # wrong token + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': force_text(url_kwargs['uid']), + 'token': '-wrong-token-' + } + self.post(url, data=data, status_code=400) + + # wrong uid + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': '-wrong-uid-', + 'token': url_kwargs['token'] + } + self.post(url, data=data, status_code=400) + + # wrong token and uid + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': '-wrong-uid-', + 'token': '-wrong-token-' + } + self.post(url, data=data, status_code=400) + + # valid payload + data = { + 'new_password1': self.NEW_PASS, + 'new_password2': self.NEW_PASS, + 'uid': force_text(url_kwargs['uid']), + 'token': url_kwargs['token'] + } + url = reverse('rest_password_reset_confirm') + self.post(url, data=data, status_code=200) + + payload = { + "username": self.USERNAME, + "password": self.NEW_PASS + } + self.post(self.login_url, data=payload, status_code=200) + + def test_password_reset_with_email_in_different_case(self): + get_user_model().objects.create_user(self.USERNAME, self.EMAIL.lower(), self.PASS) + + # call password reset in upper case + mail_count = len(mail.outbox) + payload = {'email': self.EMAIL.upper()} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count + 1) + + def test_password_reset_with_invalid_email(self): + """ + Invalid email should not raise error, as this would leak users + """ + get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + + # call password reset + mail_count = len(mail.outbox) + payload = {'email': 'nonexisting@email.com'} + self.post(self.password_reset_url, data=payload, status_code=200) + self.assertEqual(len(mail.outbox), mail_count) + + def test_user_details(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + payload = { + "username": self.USERNAME, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.token = self.response.json['key'] + self.get(self.user_url, status_code=200) + + self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) + user = get_user_model().objects.get(pk=user.pk) + self.assertEqual(user.first_name, self.response.json['first_name']) + self.assertEqual(user.last_name, self.response.json['last_name']) + self.assertEqual(user.email, self.response.json['email']) + + @override_settings(REST_USE_JWT=True) + def test_user_details_using_jwt(self): + user = get_user_model().objects.create_user(self.USERNAME, self.EMAIL, self.PASS) + payload = { + "username": self.USERNAME, + "password": self.PASS + } + self.post(self.login_url, data=payload, status_code=200) + self.token = self.response.json['access_token'] + self.get(self.user_url, status_code=200) + + self.patch(self.user_url, data=self.BASIC_USER_DATA, status_code=200) + user = get_user_model().objects.get(pk=user.pk) + self.assertEqual(user.email, self.response.json['email']) + + def test_registration(self): + user_count = get_user_model().objects.all().count() + + # test empty payload + self.post(self.register_url, data={}, status_code=400) + + result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) + self.assertIn('key', result.data) + self.assertEqual(get_user_model().objects.all().count(), user_count + 1) + + new_user = get_user_model().objects.latest('id') + self.assertEqual(new_user.username, self.REGISTRATION_DATA['username']) + + self._login() + self._logout() + + @override_settings(REST_AUTH_REGISTER_PERMISSION_CLASSES=(CustomPermissionClass,)) + def test_registration_with_custom_permission_class(self): + + class CustomRegisterView(RegisterView): + permission_classes = register_permission_classes() + authentication_classes = () + + factory = APIRequestFactory() + request = factory.post('/customer/details', self.REGISTRATION_DATA, format='json') + + response = CustomRegisterView.as_view()(request) + self.assertEqual(response.data['detail'], CustomPermissionClass.message) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + @override_settings(REST_USE_JWT=True) + def test_registration_with_jwt(self): + user_count = get_user_model().objects.all().count() + + self.post(self.register_url, data={}, status_code=400) + + result = self.post(self.register_url, data=self.REGISTRATION_DATA, status_code=201) + self.assertIn('access_token', result.data) + self.assertEqual(get_user_model().objects.all().count(), user_count + 1) + + self._login() + self._logout() def test_registration_with_invalid_password(self): data = self.REGISTRATION_DATA.copy() @@ -267,3 +514,47 @@ class APIBasicTests(TestsMixin, TestCase): self.post(self.login_url, data=payload, status_code=status.HTTP_200_OK) self.get(self.logout_url, status_code=status.HTTP_405_METHOD_NOT_ALLOWED) + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_login_jwt_sets_cookie(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertTrue('jwt-auth' in resp.cookies.keys()) + + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + def test_logout_jwt_deletes_cookie(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + self.post(self.login_url, data=payload, status_code=200) + resp = self.post(self.logout_url, status=200) + self.assertEqual('', resp.cookies.get('jwt-auth').value) + + + @override_settings(REST_USE_JWT=True) + @override_settings(JWT_AUTH_COOKIE='jwt-auth') + @override_settings(REST_FRAMEWORK=dict( + DEFAULT_AUTHENTICATION_CLASSES=[ + 'dj_rest_auth.utils.JWTCookieAuthentication' + ] + )) + @override_settings(REST_SESSION_LOGIN=False) + def test_cookie_authentication(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + get_user_model().objects.create_user(self.USERNAME, '', self.PASS) + resp = self.post(self.login_url, data=payload, status_code=200) + self.assertEqual(['jwt-auth'], list(resp.cookies.keys())) + resp = self.get('/protected-view/') + self.assertEquals(resp.status_code, 200) \ No newline at end of file diff --git a/dj_rest_auth/views.py b/dj_rest_auth/views.py index a31e879..c5ba7fc 100644 --- a/dj_rest_auth/views.py +++ b/dj_rest_auth/views.py @@ -138,7 +138,7 @@ class LogoutView(APIView): response = Response({"detail": _("Successfully logged out.")}, status=status.HTTP_200_OK) if getattr(settings, 'REST_USE_JWT', False): - cookie_name = getattr(settings, 'JWT_AUTH_COOKIE') + cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None) if cookie_name: response.delete_cookie(cookie_name) return response From 4070bce94bda8d3c3d04839f0f2952d88f95490f Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Mar 2020 06:20:44 -0500 Subject: [PATCH 10/12] Attempts to fix tests --- .circleci/config.yml | 2 +- dev-requirements.txt | 2 +- dj_rest_auth/tests/requirements.pip | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d124869..efcf792 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,9 +12,9 @@ jobs: executor: docker/docker steps: - checkout - - run: pip install -q --user coveralls djangorestframework==$DRF Django==$DJANGO_VERSION - run: pip install --user -r dev-requirements.txt - run: pip install --user -r dj_rest_auth/tests/requirements.pip + - run: pip install -q --user coveralls djangorestframework==$DRF Django==$DJANGO_VERSION - run: command: coverage run --source=dj_rest_auth setup.py test name: Test diff --git a/dev-requirements.txt b/dev-requirements.txt index cee8ea6..9cb5377 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,4 +2,4 @@ responses>=0.5.0 djangorestframework-simplejwt==4.4.0 django-allauth -coveralls>=1.11.1 \ No newline at end of file +coveralls>=1.11. \ No newline at end of file diff --git a/dj_rest_auth/tests/requirements.pip b/dj_rest_auth/tests/requirements.pip index 4ff9b2d..9f28d70 100644 --- a/dj_rest_auth/tests/requirements.pip +++ b/dj_rest_auth/tests/requirements.pip @@ -1,6 +1,4 @@ django-allauth>=0.25.0 responses>=0.3.0 flake8==2.4.0 -Django==3.0.4 djangorestframework-simplejwt==4.4.0 -djangorestframework>=3.6.4 From 27815933f52ab393477abaa94bcae0d86fee6fcf Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Mar 2020 06:23:13 -0500 Subject: [PATCH 11/12] Bumps minor version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8872a56..46f5d5f 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ f.close() setup( name='dj-rest-auth', - version='0.1.4', + version='0.2.0', author='iMerica', author_email='imichael@pm.me', url='http://github.com/jazzband/dj-rest-auth', From bfd67501048a519b6e07f9ef55e04f21a4695353 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Mar 2020 06:25:43 -0500 Subject: [PATCH 12/12] Removes coveralls patch edit --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 9cb5377..cee8ea6 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,4 +2,4 @@ responses>=0.5.0 djangorestframework-simplejwt==4.4.0 django-allauth -coveralls>=1.11. \ No newline at end of file +coveralls>=1.11.1 \ No newline at end of file