2

How can I require authentication/authorization on the tier Node field and allTiers Connection field query below?

# schema.py
class TierNode(DjangoObjectType):
    class Meta:
        model = Tier
        filter_fields = []
        interfaces = (graphene.relay.Node,)


class Query(graphene.ObjectType):
    tier = relay.Node.Field(TierNode)
    all_tiers = DjangoFilterConnectionField(TierNode)
atm
  • 1,684
  • 1
  • 22
  • 24

2 Answers2

1

You can define authorization or/and authentication decorator like this:

from functools import wraps

def authorize_required(role):
    def decorator(func):
        @wraps(func)
        def wrapper(instance, info, *args, **kwargs):
            current_user = info.context.user
            if not current_user.is_authenticated:
                raise Exception("Authentication credentials were not provided")
            if not authorize(instance, current_user, role):
                raise Exception(
                    f"{current_user} has no access to {instance} with required {role=}"
                )
            return func(instance, info, *args, **kwargs)
        return wrapper
    return decorator

def authorize(instance, user, role) -> bool:
   # check if user can have access to instance
   # if there is requirement to have certain role

And use it in schema definition:

class TierNode(DjangoObjectType):
    class Meta:
        model = Tier
        filter_fields = []
        interfaces = (graphene.relay.Node,)


class Query(graphene.ObjectType):
    tier = relay.Node.Field(TierNode)
    all_tiers = DjangoFilterConnectionField(TierNode)
  
    @authorize_required('user')
    def resolve_tier(self, info, **args):
        # some resolve code

    @authorize_required('admin')
    def resolve_all_tiers(self, info, **args):
        # some resolve code
jorzel
  • 1,216
  • 1
  • 8
  • 12
  • Thanks, @jorzel. This is very useful. I am curious to know if you actually have the `Field` resolver working, though. Try as I might, I can't get it to execute as you've defined it here. – pdoherty926 May 12 '23 at 20:20
0

You can define a resolver for those fields with auth decorator like so:

from graphql_jwt.decorators import login_required

class Query(graphene.ObjectType):
    tier = relay.Node.Field(TierNode)
    all_tiers = DjangoFilterConnectionField(TierNode)


    @login_required
    def resolve_tier(root, info, **kwargs):
        # code for resolving here

This is just using the login_decorator that comes with graphql_jwt but it will work for your custom decorators too if you defined them.

Furthermore, this also works for when you're resolving a field for TierNode:

class TierNode(DjangoObjectType):
    class Meta:
        model = Tier
        filter_fields = []
        interfaces = (graphene.relay.Node,)

    some_property = graphene.Field("types.SomePropertyType")

    @login_required
    def resolve_some_property(root, info, **kwargs):
        # code for resolving here

Jerven Clark
  • 1,191
  • 2
  • 13
  • 26
  • Thanks @jech-chua. Defining a resolver seems to work for everything besides Node and Connection fields. Maybe this is a graphene bug? – atm Sep 16 '21 at 13:33
  • What issues have you encountered with it? This is how we secure endpoints/fields in our current project. – Jerven Clark Sep 17 '21 at 00:31