mirror of
				https://github.com/Tivix/django-rest-auth.git
				synced 2025-10-30 23:37:32 +03:00 
			
		
		
		
	* Added support for REST_USE_JWT
* Added JWTSerializer * Added JWT encoding support, based on django-rest-framework-jwt * Tests for JWT authentication
This commit is contained in:
		
							parent
							
								
									4a56a9e7e5
								
							
						
					
					
						commit
						19e234d1dc
					
				|  | @ -29,10 +29,12 @@ Configuration | |||
|             ... | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| - **REST_SESSION_LOGIN** - Enable session login in Login API view (default: True) | ||||
| 
 | ||||
| 
 | ||||
| - **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 | ||||
| 
 | ||||
| - **REST_USE_JWT** - If enabled, this will use `django-rest-framework-jwt <http://getblimp.github.io/django-rest-framework-jwt/>` as a backend, and instead of session based tokens or Social Login keys, it will return a JWT.  | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ from django.conf import settings | |||
| 
 | ||||
| from rest_auth.serializers import ( | ||||
|     TokenSerializer as DefaultTokenSerializer, | ||||
|     JWTSerializer as DefaultJWTSerializer, | ||||
|     UserDetailsSerializer as DefaultUserDetailsSerializer, | ||||
|     LoginSerializer as DefaultLoginSerializer, | ||||
|     PasswordResetSerializer as DefaultPasswordResetSerializer, | ||||
|  | @ -15,6 +16,9 @@ serializers = getattr(settings, 'REST_AUTH_SERIALIZERS', {}) | |||
| TokenSerializer = import_callable( | ||||
|     serializers.get('TOKEN_SERIALIZER', DefaultTokenSerializer)) | ||||
| 
 | ||||
| JWTSerializer = import_callable( | ||||
|     serializers.get('JWT_SERIALIZER', DefaultJWTSerializer)) | ||||
| 
 | ||||
| UserDetailsSerializer = import_callable( | ||||
|     serializers.get('USER_DETAILS_SERIALIZER', DefaultUserDetailsSerializer) | ||||
| ) | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| from django.http import HttpRequest | ||||
| from django.conf import settings | ||||
| 
 | ||||
| from rest_framework.views import APIView | ||||
| from rest_framework.response import Response | ||||
| from rest_framework.permissions import AllowAny | ||||
|  | @ -13,6 +15,8 @@ from rest_auth.app_settings import TokenSerializer | |||
| from rest_auth.registration.serializers import SocialLoginSerializer | ||||
| from rest_auth.views import LoginView | ||||
| 
 | ||||
| from rest_auth.utils import jwt_encode | ||||
| 
 | ||||
| 
 | ||||
| class RegisterView(APIView, SignupView): | ||||
|     """ | ||||
|  | @ -28,7 +32,12 @@ class RegisterView(APIView, SignupView): | |||
|     permission_classes = (AllowAny,) | ||||
|     allowed_methods = ('POST', 'OPTIONS', 'HEAD') | ||||
|     token_model = Token | ||||
|     serializer_class = TokenSerializer | ||||
|      | ||||
|     def get_serializer_class(self): | ||||
|         if getattr(settings, 'REST_USE_JWT', False): | ||||
|             return JWTSerializer | ||||
|         else: | ||||
|             return TokenSerializer | ||||
| 
 | ||||
|     def get(self, *args, **kwargs): | ||||
|         return Response({}, status=status.HTTP_405_METHOD_NOT_ALLOWED) | ||||
|  | @ -38,9 +47,14 @@ class RegisterView(APIView, SignupView): | |||
| 
 | ||||
|     def form_valid(self, form): | ||||
|         self.user = form.save(self.request) | ||||
|         self.token, created = self.token_model.objects.get_or_create( | ||||
|             user=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 isinstance(self.request, HttpRequest): | ||||
|             request = self.request | ||||
|         else: | ||||
|  | @ -65,9 +79,19 @@ class RegisterView(APIView, SignupView): | |||
|             return self.get_response_with_errors() | ||||
| 
 | ||||
|     def get_response(self): | ||||
|         # serializer = self.user_serializer_class(instance=self.user) | ||||
|         serializer = self.serializer_class(instance=self.token, | ||||
|                                            context={'request': self.request}) | ||||
|         serializer_class = self.get_serializer_class() | ||||
| 
 | ||||
|         if getattr(settings, 'REST_USE_JWT', False): | ||||
|             data = { | ||||
|                 'user': self.user, | ||||
|                 'token': self.token | ||||
|             } | ||||
|             serializer = serializer_class(instance=data,  | ||||
|                                     context={'request': self.request}) | ||||
|         else: | ||||
|             serializer = serializer_class(instance=self.token, | ||||
|                                         context={'request': self.request}) | ||||
| 
 | ||||
|         return Response(serializer.data, status=status.HTTP_201_CREATED) | ||||
| 
 | ||||
|     def get_response_with_errors(self): | ||||
|  |  | |||
|  | @ -87,7 +87,6 @@ class TokenSerializer(serializers.ModelSerializer): | |||
|         model = Token | ||||
|         fields = ('key',) | ||||
| 
 | ||||
| 
 | ||||
| class UserDetailsSerializer(serializers.ModelSerializer): | ||||
| 
 | ||||
|     """ | ||||
|  | @ -98,6 +97,12 @@ class UserDetailsSerializer(serializers.ModelSerializer): | |||
|         fields = ('username', 'email', 'first_name', 'last_name') | ||||
|         read_only_fields = ('email', ) | ||||
| 
 | ||||
| class JWTSerializer(serializers.Serializer): | ||||
|     """ | ||||
|     Serializer for JWT authentication. | ||||
|     """ | ||||
|     token = serializers.CharField() | ||||
|     user = UserDetailsSerializer() | ||||
| 
 | ||||
| class PasswordResetSerializer(serializers.Serializer): | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,4 @@ | |||
| django-allauth>=0.19.1 | ||||
| responses>=0.3.0 | ||||
| flake8==2.4.0 | ||||
| djangorestframework-jwt>=1.7.2 | ||||
|  | @ -45,6 +45,13 @@ TEMPLATE_CONTEXT_PROCESSORS = [ | |||
|     "allauth.socialaccount.context_processors.socialaccount", | ||||
| ] | ||||
| 
 | ||||
| REST_FRAMEWORK = { | ||||
|     'DEFAULT_AUTHENTICATION_CLASSES': ( | ||||
|         'rest_framework.authentication.SessionAuthentication', | ||||
|         'rest_framework_jwt.authentication.JSONWebTokenAuthentication', | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| INSTALLED_APPS = [ | ||||
|     'django.contrib.admin', | ||||
|     'django.contrib.auth', | ||||
|  | @ -64,7 +71,9 @@ INSTALLED_APPS = [ | |||
|     'rest_framework.authtoken', | ||||
| 
 | ||||
|     'rest_auth', | ||||
|     'rest_auth.registration' | ||||
|     'rest_auth.registration', | ||||
| 
 | ||||
|     'rest_framework_jwt' | ||||
| ] | ||||
| 
 | ||||
| SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd" | ||||
|  |  | |||
|  | @ -90,6 +90,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 | ||||
|         } | ||||
|         # no users in db so it should throw an error | ||||
|         user = get_user_model().objects.create_user(self.USERNAME, '', self.PASS) | ||||
|         self.post(self.login_url, data=payload, status_code=200) | ||||
|         self.assertEqual('user' in self.response.json.keys(), True) | ||||
|         self.assertEqual('token' in self.response.json.keys(), True) | ||||
| 
 | ||||
| 
 | ||||
|     def test_password_change(self): | ||||
|         login_payload = { | ||||
|             "username": self.USERNAME, | ||||
|  | @ -258,6 +271,23 @@ 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_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.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']) | ||||
| 
 | ||||
|     def test_registration(self): | ||||
|         user_count = get_user_model().objects.all().count() | ||||
| 
 | ||||
|  |  | |||
|  | @ -37,7 +37,10 @@ class BaseAPITestCase(object): | |||
| 
 | ||||
|         # check_headers = kwargs.pop('check_headers', True) | ||||
|         if hasattr(self, 'token'): | ||||
|             kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token | ||||
|             if getattr(settings, 'REST_USE_JWT', False): | ||||
|                 kwargs['HTTP_AUTHORIZATION'] = 'JWT %s' % self.token | ||||
|             else: | ||||
|                 kwargs['HTTP_AUTHORIZATION'] = 'Token %s' % self.token | ||||
| 
 | ||||
|         self.response = request_func(*args, **kwargs) | ||||
|         is_json = bool( | ||||
|  |  | |||
|  | @ -126,3 +126,29 @@ class TestSocialAuth(TestCase, BaseAPITestCase): | |||
| 
 | ||||
|         self.post(self.fb_login_url, data=payload, status_code=200) | ||||
|         self.assertIn('key', self.response.json.keys()) | ||||
| 
 | ||||
|     @responses.activate | ||||
|     @override_settings( | ||||
|         REST_USE_JWT=True | ||||
|     ) | ||||
|     def test_jwt(self): | ||||
|         resp_body = '{"id":"123123123123","first_name":"John","gender":"male","last_name":"Smith","link":"https:\\/\\/www.facebook.com\\/john.smith","locale":"en_US","name":"John Smith","timezone":2,"updated_time":"2014-08-13T10:14:38+0000","username":"john.smith","verified":true}'  # noqa | ||||
|         responses.add( | ||||
|             responses.GET, | ||||
|             self.graph_api_url, | ||||
|             body=resp_body, | ||||
|             status=200, | ||||
|             content_type='application/json' | ||||
|         ) | ||||
| 
 | ||||
|         users_count = get_user_model().objects.all().count() | ||||
|         payload = { | ||||
|             'access_token': 'abc123' | ||||
|         } | ||||
| 
 | ||||
|         self.post(self.fb_login_url, data=payload, status_code=200) | ||||
|         self.assertIn('token', self.response.json.keys()) | ||||
|         self.assertIn('user', self.response.json.keys()) | ||||
|          | ||||
|         self.assertEqual(get_user_model().objects.all().count(), users_count + 1) | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,3 +9,15 @@ def import_callable(path_or_callable): | |||
|         assert isinstance(path_or_callable, string_types) | ||||
|         package, attr = path_or_callable.rsplit('.', 1) | ||||
|         return getattr(import_module(package), attr) | ||||
| 
 | ||||
| def jwt_encode(user): | ||||
|     try:  | ||||
|         from rest_framework_jwt.settings import api_settings | ||||
|     except ImportError: | ||||
|         raise ImportError('rest_framework_jwt 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) | ||||
|  |  | |||
|  | @ -13,9 +13,11 @@ from rest_framework.generics import RetrieveUpdateAPIView | |||
| from .app_settings import ( | ||||
|     TokenSerializer, UserDetailsSerializer, LoginSerializer, | ||||
|     PasswordResetSerializer, PasswordResetConfirmSerializer, | ||||
|     PasswordChangeSerializer | ||||
|     PasswordChangeSerializer, JWTSerializer | ||||
| ) | ||||
| 
 | ||||
| from .utils import jwt_encode | ||||
| 
 | ||||
| 
 | ||||
| class LoginView(GenericAPIView): | ||||
| 
 | ||||
|  | @ -31,19 +33,38 @@ class LoginView(GenericAPIView): | |||
|     permission_classes = (AllowAny,) | ||||
|     serializer_class = LoginSerializer | ||||
|     token_model = Token | ||||
|     response_serializer = TokenSerializer | ||||
| 
 | ||||
|     def get_response_serializer(self): | ||||
|         if getattr(settings, 'REST_USE_JWT', False): | ||||
|             response_serializer = JWTSerializer | ||||
|         else: | ||||
|             response_serializer = TokenSerializer | ||||
|         return response_serializer | ||||
| 
 | ||||
|     def login(self): | ||||
|         self.user = self.serializer.validated_data['user'] | ||||
|         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) | ||||
| 
 | ||||
|         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) | ||||
| 
 | ||||
|     def get_response(self): | ||||
|         return Response( | ||||
|             self.response_serializer(self.token).data, status=status.HTTP_200_OK | ||||
|         ) | ||||
|         serializer_class = self.get_response_serializer() | ||||
| 
 | ||||
|         if getattr(settings, 'REST_USE_JWT', False): | ||||
|             data = { | ||||
|                 'user': self.user, | ||||
|                 'token': self.token | ||||
|             } | ||||
|             serializer = serializer_class(instance=data) | ||||
|         else: | ||||
|             serializer = serializer_class(instance=self.token) | ||||
| 
 | ||||
|         return Response(serializer.data, status=status.HTTP_200_OK) | ||||
| 
 | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         self.serializer = self.get_serializer(data=self.request.data) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user