diff --git a/docs/configuration.rst b/docs/configuration.rst index 3746234..d726a99 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -10,6 +10,8 @@ Configuration - TOKEN_SERIALIZER - response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.TokenSerializer`` + - JWT_SERIALIZER - (Using REST_USE_JWT=True) response for successful authentication in ``rest_auth.views.LoginView``, default value ``rest_auth.serializers.JWTSerializer`` + - USER_DETAILS_SERIALIZER - serializer class in ``rest_auth.views.UserDetailsView``, default value ``rest_auth.serializers.UserDetailsSerializer`` - PASSWORD_RESET_SERIALIZER - serializer class in ``rest_auth.views.PasswordResetView``, default value ``rest_auth.serializers.PasswordResetSerializer`` @@ -42,6 +44,8 @@ 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 much 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) - **LOGOUT_ON_PASSWORD_CHANGE** - set to False if you want to keep the current user logged in after a password change diff --git a/docs/installation.rst b/docs/installation.rst index 92251dc..1118826 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -109,3 +109,18 @@ Using ``django-allauth``, ``django-rest-auth`` provides helpful class for creati ) .. note:: Starting from v0.21.0, django-allauth has dropped support for context processors. Check out http://django-allauth.readthedocs.org/en/latest/changelog.html#from-0-21-0 for more details. + + +JWT Support (optional) +---------------------- + +By default, ``django-rest-auth`` uses Django's Token-based authentication. If you want to use JWT authentication, you need to install the following: + +1. Install ``django-rest-framework-jwt`` http://getblimp.github.io/django-rest-framework-jwt/ . Right now this is the only supported JWT library. + +2. Add the following to your settings + +.. code-block:: python + + REST_USE_JWT = True + diff --git a/rest_auth/registration/views.py b/rest_auth/registration/views.py index dc96a50..30a79bb 100644 --- a/rest_auth/registration/views.py +++ b/rest_auth/registration/views.py @@ -12,6 +12,7 @@ from allauth.account.utils import complete_signup from allauth.account import app_settings as allauth_settings from rest_auth.app_settings import (TokenSerializer, + JWTSerializer, create_token) from rest_auth.registration.serializers import (SocialLoginSerializer, VerifyEmailSerializer) @@ -31,7 +32,14 @@ class RegisterView(CreateAPIView): allauth_settings.EmailVerificationMethod.MANDATORY: return {} - return TokenSerializer(user.auth_token).data + if getattr(settings, 'REST_USE_JWT', False): + data = { + 'user': user, + 'token': self.token + } + return JWTSerializer(data).data + else: + return TokenSerializer(user.auth_token).data def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) @@ -43,7 +51,10 @@ class RegisterView(CreateAPIView): def perform_create(self, serializer): user = serializer.save(self.request) - create_token(self.token_model, user, serializer) + if getattr(settings, 'REST_USE_JWT', False): + self.token = jwt_encode(user) + else: + create_token(self.token_model, user, serializer) complete_signup(self.request._request, user, allauth_settings.EMAIL_VERIFICATION, None) diff --git a/rest_auth/tests/test_api.py b/rest_auth/tests/test_api.py index f6cc839..9903c9d 100644 --- a/rest_auth/tests/test_api.py +++ b/rest_auth/tests/test_api.py @@ -91,6 +91,19 @@ class APITestCase1(TestCase, BaseAPITestCase): # test empty payload self.post(self.login_url, data={}, status_code=400) + @override_settings(REST_USE_JWT=True) + def test_login_jwt(self): + payload = { + "username": self.USERNAME, + "password": self.PASS + } + user = 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'] + + def test_login_by_email(self): # starting test without allauth app settings.INSTALLED_APPS.remove('allauth') @@ -307,6 +320,22 @@ class APITestCase1(TestCase, BaseAPITestCase): 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['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() @@ -323,6 +352,20 @@ class APITestCase1(TestCase, BaseAPITestCase): self._login() self._logout() + @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('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() data['password2'] = 'foobar' diff --git a/rest_auth/views.py b/rest_auth/views.py index 331642c..f1aa6a7 100644 --- a/rest_auth/views.py +++ b/rest_auth/views.py @@ -12,7 +12,7 @@ from rest_framework.generics import RetrieveUpdateAPIView from .app_settings import ( TokenSerializer, UserDetailsSerializer, LoginSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer, - PasswordChangeSerializer, create_token + PasswordChangeSerializer, JWTSerializer, create_token ) from .models import TokenModel @@ -43,17 +43,15 @@ class LoginView(GenericAPIView): def login(self): self.user = self.serializer.validated_data['user'] - self.token = create_token(self.token_model, self.user, self.serializer) - if getattr(settings, 'REST_SESSION_LOGIN', True): - login(self.request, self.user) - + if getattr(settings, 'REST_USE_JWT', False): self.token = jwt_encode(self.user) else: - self.token, created = self.token_model.objects.get_or_create( - user=self.user) - if getattr(settings, 'REST_SESSION_LOGIN', True): - login(self.request, self.user) + self.token = create_token(self.token_model, self.user, self.serializer) + + if getattr(settings, 'REST_SESSION_LOGIN', True): + login(self.request, self.user) + def get_response(self): serializer_class = self.get_response_serializer()