Skip to content

Instantly share code, notes, and snippets.

@orehush
Last active December 18, 2023 14:23
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save orehush/667c79b28fdc94f86746bd15694d1167 to your computer and use it in GitHub Desktop.
Save orehush/667c79b28fdc94f86746bd15694d1167 to your computer and use it in GitHub Desktop.
DRF simple JWT logout flow
from django.utils.text import gettext_lazy as _
from rest_framework import serializers
from rest_framework_simplejwt.tokens import RefreshToken, TokenError
class RefreshTokenSerializer(serializers.Serializer):
refresh = serializers.CharField()
default_error_messages = {
'bad_token': _('Token is invalid or expired')
}
def validate(self, attrs):
self.token = attrs['refresh']
return attrs
def save(self, **kwargs):
try:
RefreshToken(self.token).blacklist()
except TokenError:
self.fail('bad_token')
from functools import partial
from rest_framework.test import APITestCase
from rest_framework.reverse import reverse
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework_simplejwt.tokens import RefreshToken
from apps.account.models import User
class TestLoginCase(APITestCase):
login_url = reverse('token_obtain_pair')
refresh_token_url = reverse('token_refresh')
logout_url = reverse('logout')
email = 'test@user.com'
password = 'kah2ie3urh4k'
def setUp(self):
self.user = User.objects.create_user(self.email, self.password)
def _login(self):
data = {
'email': self.email, 'password': self.password
}
r = self.client.post(self.login_url, data)
body = r.json()
if 'access' in body:
self.client.credentials(
HTTP_AUTHORIZATION='Bearer %s' % body['access'])
return r.status_code, body
def test_logout_response_200(self):
_, body = self._login()
data = {'refresh': body['refresh']}
r = self.client.post(self.logout_url, data)
body = r.content
self.assertEquals(r.status_code, 204, body)
self.assertFalse(body, body)
def test_logout_with_bad_refresh_token_response_400(self):
self._login()
data = {'refresh': 'dsf.sdfsdf.sdf'}
r = self.client.post(self.logout_url, data)
body = r.json()
self.assertEquals(r.status_code, 400, body)
self.assertTrue(body, body)
def test_logout_refresh_token_in_blacklist(self):
_, body = self._login()
r = self.client.post(self.logout_url, body)
token = partial(RefreshToken, body['refresh'])
self.assertRaises(TokenError, token)
def test_access_token_still_valid_after_logout(self):
_, body = self._login()
self.client.post(self.logout_url, body)
r = self.client.get(self.profile_url)
body = r.json()
self.assertEquals(r.status_code, 200, body)
self.assertTrue(body, body)
def test_access_token_invalid_in_hour_after_logout(self):
_, body = self._login()
self.client.post(self.logout_url, body)
m = mock.Mock()
m.return_value = aware_utcnow() + timedelta(minutes=60)
with mock.patch('rest_framework_simplejwt.tokens.aware_utcnow', m):
r = self.client.get(self.profile_url)
body = r.json()
self.assertEquals(r.status_code, 401, body)
self.assertTrue(body, body)
from rest_framework import permissions, status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .serializers import RefreshTokenSerializer
class LogoutView(GenericAPIView):
serializer_class = RefreshTokenSerializer
permission_classes = (permissions.IsAuthenticated, )
def post(self, request, *args):
sz = self.get_serializer(data=request.data)
sz.is_valid(raise_exception=True)
sz.save()
return Response(status=status.HTTP_204_NO_CONTENT)
@abhishekkumaresan
Copy link

HI,
I am getting AttributeError: 'RefreshToken' object has no attribute 'blacklist' in sz.save(), Can you please tell me where I could be wrong

Thank you.

@orehush
Copy link
Author

orehush commented Mar 13, 2020

HI @abhishekkumaresan
You should add
'rest_framework_simplejwt.token_blacklist' to INSTALLED_APPS
in your settings

@abhishekkumaresan
Copy link

abhishekkumaresan commented Mar 15, 2020

Thank you @orehush, I missed it from the read me

@cosmos-sajal
Copy link

Hey, first of all, really thankful to you for this! It helped a lot.
Secondly, this blacklists the refresh token, is there a workaround or some way to invalidate the access token as well?

@orehush
Copy link
Author

orehush commented Apr 4, 2020

Hi @cosmos-sajal

I think there is no workaround on how to add an access token to the blacklist. Instead, you should configure properly your authentication and make deal with frontend guy. Because the aim for using JWT is making access token with a short lifetime and without needing fetching data from DB on each request (like django session or drf token authentication works).
Please, see disscussion

@cosmos-sajal
Copy link

cosmos-sajal commented Apr 4, 2020

I understand, thank you so much @orehush for the immense help :)

@Khumoyun098
Copy link

Khumoyun098 commented Oct 19, 2020

Hi! @orehush
I am using @orehush this gist and
I got a erorr Forbidden (CSRF cookie not set.): /api/user/logout/

can you help me?

@adelekecode
Copy link

Hi @orehush is there a way I can contact you, your mail or cell please it really important.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment