Skip to content

Commit

Permalink
Merge pull request #28 from mjlabe/logout-blacklist-jwt-token
Browse files Browse the repository at this point in the history
Refresh token not blacklisted on logout
  • Loading branch information
iMerica committed Apr 10, 2020
2 parents 058df2b + 1c64c0d commit 275d1c4
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ target/
# Jupyter Notebook
.ipynb_checkpoints

# IDE
.idea

# pyenv
.python-version

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ REST_USE_JWT = True
JWT_AUTH_COOKIE = 'jwt-auth'
```

### Testing

To run the tests within a virtualenv, run `python runtests.py` from the repository directory.
The easiest way to run test coverage is with [`coverage`](https://pypi.org/project/coverage/),
which runs the tests against all supported Django installs. To run the test coverage
within a virtualenv, run `coverage run ./runtests.py` from the repository directory then run `coverage report`.


### Documentation
Expand Down
2 changes: 2 additions & 0 deletions dj_rest_auth/tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@

'dj_rest_auth',
'dj_rest_auth.registration',

'rest_framework_simplejwt.token_blacklist'
]

SECRET_KEY = "38dh*skf8sjfhs287dh&^hd8&3hdg*j2&sd"
Expand Down
47 changes: 45 additions & 2 deletions dj_rest_auth/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from allauth.account import app_settings as account_app_settings
from dj_rest_auth.registration.app_settings import register_permission_classes
from dj_rest_auth.registration.views import RegisterView
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core import mail
Expand All @@ -9,6 +9,8 @@
from rest_framework import status
from rest_framework.test import APIRequestFactory

from dj_rest_auth.registration.app_settings import register_permission_classes
from dj_rest_auth.registration.views import RegisterView
from .mixins import CustomPermissionClass, TestsMixin

try:
Expand Down Expand Up @@ -555,3 +557,44 @@ def test_cookie_authentication(self):
self.assertEqual(['jwt-auth'], list(resp.cookies.keys()))
resp = self.get('/protected-view/')
self.assertEquals(resp.status_code, 200)

@override_settings(REST_USE_JWT=True)
def test_blacklisting_not_installed(self):
settings.INSTALLED_APPS.remove('rest_framework_simplejwt.token_blacklist')
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)
token = resp.data['refresh_token']
resp = self.post(self.logout_url, status=200, data={'refresh': token})
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data["detail"],
"Neither cookies or blacklist are enabled, so the token has not been deleted server side. "
"Please make sure the token is deleted client side.")

@override_settings(REST_USE_JWT=True)
def test_blacklisting(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)
token = resp.data['refresh_token']
# test refresh token not included in request data
resp = self.post(self.logout_url, status=200)
self.assertEqual(resp.status_code, 401)
# test token is invalid or expired
resp = self.post(self.logout_url, status=200, data={'refresh': '1'})
self.assertEqual(resp.status_code, 401)
# test successful logout
resp = self.post(self.logout_url, status=200, data={'refresh': token})
self.assertEqual(resp.status_code, 200)
# test token is blacklisted
resp = self.post(self.logout_url, status=200, data={'refresh': token})
self.assertEqual(resp.status_code, 401)
# test other TokenError, AttributeError, TypeError (invalid format)
resp = self.post(self.logout_url, status=200, data=json.dumps({'refresh': token}))
self.assertEqual(resp.status_code, 500)
35 changes: 35 additions & 0 deletions dj_rest_auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework_simplejwt.tokens import RefreshToken

from .app_settings import (JWTSerializer, LoginSerializer,
PasswordChangeSerializer,
Expand Down Expand Up @@ -132,15 +134,48 @@ def logout(self, request):
request.user.auth_token.delete()
except (AttributeError, ObjectDoesNotExist):
pass

if getattr(settings, 'REST_SESSION_LOGIN', True):
django_logout(request)

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', None)
if cookie_name:
response.delete_cookie(cookie_name)

elif 'rest_framework_simplejwt.token_blacklist' in settings.INSTALLED_APPS:
# add refresh token to blacklist
try:
token = RefreshToken(request.data['refresh'])
token.blacklist()

except KeyError:
response = Response({"detail": _("Refresh token was not included in request data.")},
status=status.HTTP_401_UNAUTHORIZED)

except (TokenError, AttributeError, TypeError) as error:
if hasattr(error, 'args'):
if 'Token is blacklisted' in error.args or 'Token is invalid or expired' in error.args:
response = Response({"detail": _(error.args[0])},
status=status.HTTP_401_UNAUTHORIZED)

else:
response = Response({"detail": _("An error has occurred.")},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)

else:
response = Response({"detail": _("An error has occurred.")},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)

else:
response = Response({
"detail": _("Neither cookies or blacklist are enabled, so the token has not been deleted server "
"side. Please make sure the token is deleted client side."
)}, status=status.HTTP_200_OK)

return response


Expand Down

0 comments on commit 275d1c4

Please sign in to comment.