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 663 forks source link

JWT for object level permission & Discussion tab #766

Closed LiorA1 closed 11 months ago

LiorA1 commented 11 months ago

Hi,

I understand that this library is not for authentication, but for authorization.

So, after a successful login and getting the access token, can we also use this library to associate a user to an object? Or to verify that only the owner user can change his own objects ? (lets say posts)

And If so, what is the recommended way?

In other words I thinking how to implement this:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

But using 'simplejwt'. I noticed that request contains the following: '_user': <User: liora>, '_auth': {'token_type': 'access', 'exp': 1701112655, 'iat': 1701112355, 'jti': 'ef471a0dd43d47f9b77d6569020eb2d1', 'user_id': 1}

So maybe to use those attributes ?

Would like to hear any suggestions.

P.S- Maybe its a good idea to open a discussion tab for these kind of questions?

Thanks!

LiorA1 commented 11 months ago

Hi,

I think this will provide an object level permission:

class IsOwnerOrReadOnlyJWT(permissions.BasePermission):
    """
    Custom JWT permission to only allow owners of an object to edit it.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        jwt_authenticator = JWTAuthentication()
        data = jwt_authenticator.authenticate(request=request)
        if data:
            user, token = data
            if user and user.id == request.user.id == obj.owner_id:
                # Write permissions are only allowed to the owner of the object.
                return True

        # If wasn't safe method or authenticate as the owner of the object - reject.
        return False

like to hear opinions, Thanks!

Andrew-Chen-Wang commented 11 months ago

Hi! Great approach, and I think that's a great custom solution. I think if you want an authentication battery included package, you should take a look at django-oauth-toolkit which will come with its own claims.

I also think that, with JWTs, you could simply add claims to your JWT rather than performing backend checks for resources for everything. Some things require permissions, such as at the object level like what can the current user see, but that should be at view level.

suspiciousRaccoon commented 10 months ago

Hi @Andrew-Chen-Wang, even if authentication isn't the main goal of this library, do you think a section in the docs with an example would be a good idea? My use case doesn't require oauth and a simple extra permission does the job. I also think adding the discussions tab is a good idea. A lot of the issues aren't really issues, but people asking how to do X or Y.

Andrew-Chen-Wang commented 10 months ago

Happy to review a new doc section with multiple examples. Might be helpful to link to examples written in the code base instead of the docs, and embed the code.

Also happy to open Discussions tab. @jazzband/roadies are the only ones with power to do so.

LiorA1 commented 10 months ago

@Andrew-Chen-Wang

Hi! Great approach, and I think that's a great custom solution. I think if you want an authentication battery included package, you should take a look at django-oauth-toolkit which will come with its own claims.

I also think that, with JWTs, you could simply add claims to your JWT rather than performing backend checks for resources for everything. Some things require permissions, such as at the object level like what can the current user see, but that should be at view level.

I will take a look at django-oauth-toolkit ,thanks! Can you elaborate about "simply add claims to your JWT" ?

(Sorry for the delay, had some home-assignments / interviews)

Andrew-Chen-Wang commented 10 months ago

the internet is async, so nw!

jwts have public claims and private payloads. Feel free to read the docs to see how to set each.

LiorA1 commented 10 months ago

Didn't understand how it could replace the above. Or you meant to do this check in the views? (It will be DRY?)

Andrew-Chen-Wang commented 10 months ago

Either is fine, serializer is preferred. Doc page: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/customizing_token_claims.html