jazzband / djangorestframework-simplejwt

A JSON Web Token authentication plugin for the Django REST Framework.
https://django-rest-framework-simplejwt.readthedocs.io/
MIT License
4.01k stars 662 forks source link

TokenBlacklistView should require access token #713

Open Chhunneng opened 1 year ago

Chhunneng commented 1 year ago

as we use TokenBlacklistView to log out users but I recognize that it does not require an access token to log out. I think It should.

tpotjj commented 1 year ago

Agree, I have implemented this in my own APIView this way:

class Logout(APIView):
    """
    The API for logging out a user -> blacklisting tokens
    """
    permission_classes = (IsAuthenticated, )
    serializer_class = LogoutSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        return self.valid_request_data(serializer.validated_data, request)

    def valid_request_data(self, data, request):
        refresh_token = data["refresh_token"]
        user = request.user

        with transaction.atomic():
           user.blacklist_token(refresh_token)

        return ApiMessageResponse(_("Logged out succesfully"), status=status.HTTP_200_OK)
Chhunneng commented 1 year ago

Agree, I have implemented this in my own APIView this way:

class Logout(APIView):
    """
    The API for logging out a user -> blacklisting tokens
    """
    permission_classes = (IsAuthenticated, )
    serializer_class = LogoutSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        return self.valid_request_data(serializer.validated_data, request)

    def valid_request_data(self, data, request):
        refresh_token = data["refresh_token"]
        user = request.user

        with transaction.atomic():
           user.blacklist_token(refresh_token)

        return ApiMessageResponse(_("Logged out succesfully"), status=status.HTTP_200_OK)

Can I ask user.blacklist_token(refresh_token), Do the users have this function auto or need to override? And can I see your LogoutSerializer?

tpotjj commented 1 year ago

The blacklist_token is a method that I added to the User model (class) myself:

    def blacklist_token(self, token):
        outstanding_token = OutstandingToken.objects.get(token=token)
        BlacklistedToken.objects.create(token=outstanding_token)

And here is the LogoutSerializer:

class LogoutSerializer(serializers.Serializer):
    refresh_token = serializers.CharField(allow_blank=False)

Nothing special actually, pretty basic APIView and Serializer :)

Chhunneng commented 1 year ago

The blacklist_token is a method that I added to the User model (class) myself:

    def blacklist_token(self, token):
        outstanding_token = OutstandingToken.objects.get(token=token)
        BlacklistedToken.objects.create(token=outstanding_token)

And here is the LogoutSerializer:

class LogoutSerializer(serializers.Serializer):
    refresh_token = serializers.CharField(allow_blank=False)

Nothing special actually, pretty basic APIView and Serializer :)

After add refresh token to blacklist, the access token is still usable!

tpotjj commented 1 year ago

@Chhunneng correct, but only for the time it is issued with, which is a configuration you can manage yourself. I've set this to 1min, which means that the client has a maximum of 60 seconds to make requests with the access token they have.

Since the client, in my case a Vue/Nuxt application, I have control over the cookies & local storage. If a user presses log-out, we both delete all the tokens on the client, as well as blacklist the refresh token.

Since JWT is created with a stateless idea in mind, this would be the furthest I want to go personally. Otherwise, you could just use the built-in Django Authentication system with the Token system they provide out of the box.

Maybe it would be good to have a look at this issue: https://github.com/jazzband/djangorestframework-simplejwt/issues/371

expzhizhuo commented 1 year ago

I want to ask if your login method was rewritten by yourself, not using Djangos built-in login, but I used the login that Djangos own, I used your method to set the usermodel, as the user logout, It reports the error "OutstandingToken matching query does not exist.", which looks like the 'OutstandingToken' object does not exist, causing it to report an error

tpotjj commented 1 year ago

@zhizhuoshuma I have created my own logic regarding login and authentication. Providing a user with the JWT token is done with refresh = RefreshToken.for_user(self)

Now, copy/pasting everything here is a little bit redundant, so I would rather drop a link to my Repo here which you can use to figure out the missing parts: https://github.com/QuantTrade-io/qt-api

Under qt_auth -> views.py -> Line 161, you'll find the actual Login API endpoint.

If you have any further questions, feel free to ask them, I'm glad to help you out!

expzhizhuo commented 1 year ago

Thank you very much. I have seen it. Do you have wechat? Can we add a friend? Look forward to your reply

tpotjj commented 1 year ago

@zhizhuoshuma I don't have WeChat, I do have Telegram or Twitter: https://twitter.com/tpotjj