From c9c5534cf02c5374e385866b11608714f3bea5fa Mon Sep 17 00:00:00 2001 From: Anatoly Date: Mon, 12 Aug 2019 12:55:08 +0300 Subject: [PATCH] version 0.0.5.10: added endpoint to refresh token --- apps/authorization/serializers/common.py | 5 ++++ apps/authorization/urls/common.py | 2 ++ apps/authorization/views/common.py | 35 ++++++++++++++++++++++++ apps/utils/permissions.py | 21 ++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 apps/utils/permissions.py diff --git a/apps/authorization/serializers/common.py b/apps/authorization/serializers/common.py index 6304d9c9..417352c3 100644 --- a/apps/authorization/serializers/common.py +++ b/apps/authorization/serializers/common.py @@ -75,6 +75,11 @@ class LoginSerializer(BaseAuthSerializerMixin, serializers.ModelSerializer): fields = ('username', 'password', 'source') +class RefreshTokenSerializer(BaseAuthSerializerMixin): + """Serializer for refresh token view""" + refresh_token = serializers.CharField(write_only=True) + + class LoginByEmailSerializer(LoginSerializerMixin, serializers.ModelSerializer): """Serializer for signing up user by email""" email = serializers.EmailField(write_only=True) diff --git a/apps/authorization/urls/common.py b/apps/authorization/urls/common.py index de7bf54e..d0ac84ce 100644 --- a/apps/authorization/urls/common.py +++ b/apps/authorization/urls/common.py @@ -46,6 +46,8 @@ urlpatterns_api = [ # logout path('logout/', views.LogoutView.as_view(), name="logout"), path('revoke-token/', views.RevokeTokenView.as_view(), name="revoke-token"), + # refresh token + path('refresh-token/', views.RefreshTokenView.as_view(), name="refresh-token"), ] urlpatterns = urlpatterns_api + \ diff --git a/apps/authorization/views/common.py b/apps/authorization/views/common.py index f7bb8897..73e32744 100644 --- a/apps/authorization/views/common.py +++ b/apps/authorization/views/common.py @@ -17,6 +17,7 @@ from rest_framework_social_oauth2.oauth2_endpoints import SocialTokenServer from authorization.models import Application from authorization.serializers import common as serializers from utils import exceptions as utils_exceptions +from utils import permissions as utils_permissions # Mixins @@ -227,6 +228,40 @@ class RevokeTokenView(OAuth2ViewMixin, GenericAPIView): return response +class RefreshTokenView(OAuth2ViewMixin, GenericAPIView): + """ + Implements an endpoint to provide access tokens + + The endpoint is used in the following flows: + * Authorization code + * Password + * Client credentials + """ + permission_classes = (utils_permissions.IsAuthenticatedAndHasRefreshToken, ) + serializer_class = serializers.RefreshTokenSerializer + + def post(self, request, *args, **kwargs): + # Preparing request data + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + request_data = self.prepare_request_data(serializer.validated_data) + request_data.update({ + 'grant_type': 'refresh_token', + 'refresh_token': serializer.validated_data.get('refresh_token') + }) + # Use the rest framework `.data` to fake the post body of the django request. + request._request.POST = request._request.POST.copy() + for key, value in request_data.items(): + request._request.POST[key] = value + + url, headers, body, oauth2_status = self.create_token_response(request._request) + response = Response(data=json.loads(body), status=oauth2_status) + + for k, v in headers.items(): + response[k] = v + return response + + # Utils class TokenView(CsrfExemptMixin, OAuthLibMixin, GenericAPIView): """ diff --git a/apps/utils/permissions.py b/apps/utils/permissions.py new file mode 100644 index 00000000..8f0a5b6c --- /dev/null +++ b/apps/utils/permissions.py @@ -0,0 +1,21 @@ +"""Project custom permissions""" +from rest_framework.permissions import BasePermission + + +class IsAuthenticatedAndHasRefreshToken(BasePermission): + """ + Check if requested user is authenticated and has refresh token + """ + + def has_permission(self, request, view): + token = request.data.get('refresh_token') + user = request.user + if token and hasattr(user, 'oauth2_provider_refreshtoken'): + refresh_token_qs = user.oauth2_provider_refreshtoken + return ( + user.is_authenticated and + user.is_active and + refresh_token_qs.filter(token=token).exists() + ) + else: + return False