If the user changes his/her password, the old refresh token can still be used to generate new access tokens.
How to make refresh tokens invalid if username or password is changed
In my opinion refresh token should be blacklisted in this case.
Also RefreshToken should be blacklisted in case of new authentication IMHO
It's not the best UX, but you could delete the tokens from local storage on the frontend and force the user to log in again.
Something like (In React-Redux):
localStorage.removeItem('refresh_token');
This is my custom implementation of blacklist on consequent logins:
from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken, OutstandingToken
def logout(user):
for token in OutstandingToken.objects.filter(user=user).exclude(
id__in=BlacklistedToken.objects.filter(token__user=user).values_list('token_id', flat=True),
):
BlacklistedToken.objects.create(token=token)
class BaseTokenObtainPairSerializer(simplejwt_serializers.TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
logout(user=user)
token = super(BaseTokenObtainPairSerializer, cls).get_token(user)
return token
But the above method will blacklist all the refresh tokens of the user. What should I do to invalidate the current refresh token only?
@Aniket-Singla You can get the current token from _request.user_ I think. Then you can use the corresponding outstanding token to blacklist it.
However IMHO, all tokens should be blacklisted if user changes her password/username. I made a signal for this .
@adwait-thattey I already tried getting user from the request. But I did not find any way to only logout user from current device. Please suggest how to delete the current device token. I am trying to write logout functionality and not the password reset scenario.
I've ended up posting the refresh token to a DRF APIView while also removing it from the backend. If the token is in the headers, that may be a more straightforward approach.
// authAPI.js
static async logout() {
try {
const refresh_token = localStorage.getItem('refresh_token');
const response = await axiosInstance.post('/api/user/token/blacklist/', {
refresh_token: refresh_token
});
axiosInstance.defaults.headers['Authorization'] = null;
return response
} catch (error) {
throw error;
}
}
And the Django code:
from rest_framework.views import APIView
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
from rest_framework import permissions, status
class LogoutAndBlacklistRefreshTokenForUserView(APIView):
permission_classes = (permissions.AllowAny,)
authentication_classes = ()
def delete(self, request):
# find all tokens by user and blacklists them, forcing them to log out.
try:
tokens = OutstandingToken.objects.filter(user=request.user)
for token in tokens:
token = RefreshToken(token.token)
token.blacklist()
except:
token = RefreshToken(request.data.refresh_token)
token.blacklist()
return Response(status=status.HTTP_205_RESET_CONTENT) # 204 means no content, 205 means no content and refresh
def post(self, request):
# Post is for logging out in current browser
try:
refresh_token = request.data["refresh_token"]
token = RefreshToken(refresh_token)
token.blacklist()
return Response(status=status.HTTP_205_RESET_CONTENT)
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
Thanks for helping @Toruitas . I will try to blacklist the token by getting it from the header.
I reached at the following logout method:
class LogoutView(views.APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, *args, **kwargs):
return self.logout_current_session(request)
def logout_current_session(self, request, *args, **kwargs):
# Post is for logging out in current browser
try:
refresh_token = request.data["refresh_token"]
token = OutstandingToken.objects.get(token=refresh_token)
if token.user == request.user:
BlacklistedToken.objects.create(token=token)
return Response(status=status.HTTP_205_RESET_CONTENT)
except:
return Response(status=status.HTTP_400_BAD_REQUEST)
I am taking the refresh_token in post body. Token will be deleted only if the submitted token belongs to the current logged in user. Permission classes is also set to IsAuthenticated .
Thanks for the inputs.
Most helpful comment
I've ended up posting the refresh token to a DRF APIView while also removing it from the backend. If the token is in the headers, that may be a more straightforward approach.
And the Django code: