Authorization in Django

There are several ways you may want to limit access to data when working with Graphene and Django: limiting which fields are accessible via GraphQL and limiting which objects a user can access.

Let’s use a simple example model.

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    published = models.BooleanField(default=False)
    owner = models.ForeignKey('auth.User')

Limiting Field Access

To limit fields in a GraphQL query simply use the fields meta attribute.

from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        fields = ('title', 'content')
        interfaces = (relay.Node, )

conversely you can use exclude meta attribute.

from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        exclude = ('published', 'owner')
        interfaces = (relay.Node, )

Queryset Filtering On Lists

In order to filter which objects are available in a queryset-based list, define a resolve method for that field and return the desired queryset.

from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post

class Query(ObjectType):
    all_posts = DjangoFilterConnectionField(PostNode)

    def resolve_all_posts(self, info):
         return Post.objects.filter(published=True)

User-based Queryset Filtering

If you are using GraphQLView you can access Django’s request with the context argument.

from graphene import ObjectType
from graphene_django.filter import DjangoFilterConnectionField
from .models import Post

class Query(ObjectType):
    my_posts = DjangoFilterConnectionField(PostNode)

    def resolve_my_posts(self, info):
        # context will reference to the Django request
        if not info.context.user.is_authenticated:
            return Post.objects.none()
        else:
            return Post.objects.filter(owner=info.context.user)

If you’re using your own view, passing the request context into the schema is simple.

result = schema.execute(query, context_value=request)

Global Filtering

If you are using DjangoObjectType you can define a custom get_queryset.

from graphene import relay
from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post

    @classmethod
    def get_queryset(cls, queryset, info):
        if info.context.user.is_anonymous:
            return queryset.filter(published=True)
        return queryset

Filtering ID-based Node Access

In order to add authorization to id-based node access, we need to add a method to your DjangoObjectType.

from graphene_django.types import DjangoObjectType
from .models import Post

class PostNode(DjangoObjectType):
    class Meta:
        model = Post
        fields = ('title', 'content')
        interfaces = (relay.Node, )

    @classmethod
    def get_node(cls, info, id):
        try:
            post = cls._meta.model.objects.get(id=id)
        except cls._meta.model.DoesNotExist:
            return None

        if post.published or info.context.user == post.owner:
            return post
        return None

Adding Login Required

To restrict users from accessing the GraphQL API page the standard Django LoginRequiredMixin can be used to create your own standard Django Class Based View, which includes the LoginRequiredMixin and subclasses the GraphQLView.:

# views.py

from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView


class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
    pass

After this, you can use the new PrivateGraphQLView in the project’s URL Configuration file url.py:

For Django 1.11:

urlpatterns = [
  # some other urls
  url(r'^graphql$', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]

For Django 2.0 and above:

urlpatterns = [
  # some other urls
  path('graphql', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]