Skip to content

Commit

Permalink
Adds view for refreshing tokens with cookies (#173)
Browse files Browse the repository at this point in the history
  • Loading branch information
iMerica committed Nov 19, 2020
1 parent 5301f1e commit 441b2e9
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 16 deletions.
32 changes: 31 additions & 1 deletion dj_rest_auth/jwt_auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
from django.conf import settings
from rest_framework_simplejwt.authentication import JWTAuthentication
from django.utils import timezone
from rest_framework import exceptions
from rest_framework.authentication import CSRFCheck
from rest_framework_simplejwt.authentication import JWTAuthentication


def get_refresh_view():
""" Returns a Token Refresh CBV without a circular import """
from rest_framework_simplejwt.settings import api_settings as jwt_settings
from rest_framework_simplejwt.views import TokenRefreshView

class RefreshViewWithCookieSupport(TokenRefreshView):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
cookie_name = getattr(settings, 'JWT_AUTH_COOKIE', None)
if cookie_name and response.status_code == 200 and 'access' in response.data:
cookie_secure = getattr(settings, 'JWT_AUTH_SECURE', False)
cookie_httponly = getattr(settings, 'JWT_AUTH_HTTPONLY', True)
cookie_samesite = getattr(settings, 'JWT_AUTH_SAMESITE', 'Lax')
token_expiration = (timezone.now() + jwt_settings.ACCESS_TOKEN_LIFETIME)
response.set_cookie(
cookie_name,
response.data['access'],
expires=token_expiration,
secure=cookie_secure,
httponly=cookie_httponly,
samesite=cookie_samesite,
)

response.data['access_token_expiration'] = token_expiration
return response
return RefreshViewWithCookieSupport


class JWTCookieAuthentication(JWTAuthentication):
"""
Expand Down
20 changes: 20 additions & 0 deletions dj_rest_auth/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,3 +913,23 @@ def test_refresh_cookie_name(self):
self.assertIn('xxx', resp.cookies.keys())
self.assertIn('refresh-xxx', resp.cookies.keys())
self.assertEqual(resp.cookies.get('refresh-xxx').get('path'), '/foo/bar')

@override_settings(JWT_AUTH_RETURN_EXPIRATION=True)
@override_settings(REST_USE_JWT=True)
@override_settings(JWT_AUTH_COOKIE='xxx')
@override_settings(JWT_AUTH_REFRESH_COOKIE='refresh-xxx')
def test_custom_token_refresh_view(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)
refresh = resp.data.get('refresh_token')
refresh_resp = self.post(
reverse('token_refresh'),
data=dict(refresh=refresh),
status_code=200
)
self.assertIn('xxx', refresh_resp.cookies)
23 changes: 14 additions & 9 deletions dj_rest_auth/tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
from allauth.socialaccount.providers.facebook.views import \
FacebookOAuth2Adapter
from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter
from django.conf.urls import include, url
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic import TemplateView
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenVerifyView

from dj_rest_auth.jwt_auth import get_refresh_view
from dj_rest_auth.registration.views import (SocialAccountDisconnectView,
SocialAccountListView,
SocialConnectView,
SocialLoginView)
from dj_rest_auth.social_serializers import (TwitterConnectSerializer,
TwitterLoginSerializer)
from dj_rest_auth.urls import urlpatterns
from django.conf.urls import include, url
from django.views.generic import TemplateView
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView

from . import django_urls

Expand Down Expand Up @@ -64,6 +66,7 @@ def twitter_login_view(request):
class TwitterLoginNoAdapter(SocialLoginView):
serializer_class = TwitterLoginSerializer


@ensure_csrf_cookie
@api_view(['GET'])
def get_csrf_cookie(request):
Expand All @@ -89,4 +92,6 @@ def get_csrf_cookie(request):
name='social_account_disconnect'),
url(r'^accounts/', include('allauth.socialaccount.urls')),
url(r'^getcsrf/', get_csrf_cookie, name='getcsrf'),
]
url('^token/verify/', TokenVerifyView.as_view(), name='token_verify'),
url('^token/refresh/', get_refresh_view().as_view(), name='token_refresh'),
]
13 changes: 7 additions & 6 deletions dj_rest_auth/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.conf import settings
from django.urls import path

from dj_rest_auth.views import (LoginView, LogoutView, PasswordChangeView,
PasswordResetConfirmView, PasswordResetView,
UserDetailsView)
from django.urls import path
from django.conf import settings

urlpatterns = [
# URLs that do not require a session or valid token
Expand All @@ -16,11 +17,11 @@
]

if getattr(settings, 'REST_USE_JWT', False):
from rest_framework_simplejwt.views import (
TokenRefreshView, TokenVerifyView,
)
from rest_framework_simplejwt.views import TokenVerifyView

from dj_rest_auth.jwt_auth import get_refresh_view

urlpatterns += [
path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('token/refresh/', get_refresh_view().as_view(), name='token_refresh'),
]

0 comments on commit 441b2e9

Please sign in to comment.