2

I'm using Django Rest Framework and OAuthTookit.

I want that the scope provided by the token should be HTTP Method specific. For eg:- GET, PUT, DELETE of the same APIView should have different scopes.

Following are my APIs.

class MyView(RetrieveUpdateDestroyAPIView):
    permission_classes = [TokenHasScope]
    required_scopes = ['scope1']
    serializer_class = ModelSerializer
    queryset = Model.objects.all()

Currently, the scope is set at the class level, which means to access all the GET, PUT & DELETE method, the token should have scope1.

I want that there should be different scope for different HTTP methods. How can I set different scope for different methods?

Praful Bagai
  • 16,684
  • 50
  • 136
  • 267

3 Answers3

3

To handle this case, I think you need to implement a new permission class, something like this:

class TokenHasScopeForMethod(TokenHasScope):

     def has_permission(self, request, view):
         token = request.auth

         if not token:
             return False

         if hasattr(token, "scope"):
             # Get the scopes required for the current method from the view
             required_scopes = view.required_scopes_per_method[request.method]

             return token.is_valid(required_scopes)

And use it in your view like this:

class MyView(RetrieveUpdateDestroyAPIView):
     permission_classes = [TokenHasScopeForMethod]
     required_scopes_per_method = {'POST': ['post_scope'], 'GET': ['get_scope']}
     serializer_class = ModelSerializer
     queryset = Model.objects.all()
Clément Denoix
  • 1,504
  • 11
  • 18
3

Perhaps You can use TokenMatchesOASRequirements permission class

class SongView(views.APIView):
    authentication_classes = [OAuth2Authentication]
    permission_classes = [TokenMatchesOASRequirements]
    required_alternate_scopes = {
        "GET": [["read"]],
        "POST": [["create"], ["post", "widget"]],
        "PUT":  [["update"], ["put", "widget"]],
        "DELETE": [["delete"], ["scope2", "scope3"]],
    }
sahama
  • 669
  • 8
  • 16
0

I like @clément-denoix answer, but would tweak it a bit.

Instead of reloading TokenHasScope.has_permission I would suggest to redefine TokenHasScope.get_scopes as it is smaller method and perfectly fit for what you need. Also original has_permission method has additional logic that I prefer to preserve.

Something like this should do the trick:

from django.core.exceptions import ImproperlyConfigured
from oauth2_provider.contrib.rest_framework import TokenHasScope


class TokenHasScopeForMethod(TokenHasScope):
    def get_scopes(self, request, view):

        try:
            scopes = getattr(view, "required_scopes_per_method")
            return scopes[request.method]
        except (AttributeError, KeyError):
            raise ImproperlyConfigured(
                "TokenHasScope requires the view to define the required_scopes_per_method attribute"
            )
wolendranh
  • 4,202
  • 1
  • 28
  • 37