flavors / django-graphql-jwt

JSON Web Token (JWT) authentication for Graphene Django
https://django-graphql-jwt.domake.io
MIT License
819 stars 171 forks source link

Protect GraphQL view (for example schema definition) to require login #254

Open LinnaViljami opened 3 years ago

LinnaViljami commented 3 years ago

Problem description:

django_graphql_jwt provides nice decorators to protect queries and mutations. However there is no easy way to protect GraphQL view to non-authenticated users. When using basic django authentication this problem could be solved using mixin classes or decorators:

# Using decorators
from django.contrib.auth.decorators import login_required
path('graphql/', login_required(GraphQLView.as_view(graphiql=True)))

# Using mixin classes
from django.contrib.auth.mixins import LoginRequiredMixin
from graphene_django.views import GraphQLView

class PrivateGraphQLView(LoginRequiredMixin, GraphQLView):
    """Adds a login requirement to graphQL API access via main endpoint."""
    pass

However these solutions are not working when using JWT tokens.

This problem leads to a situation where anonymous users can view my whole schema definition and maybe search vulnerabilities. Current situation:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),

Possible solutions:

Provide decorators or mixin classes that can be used as in standard django.contrib.auth module

mongkok commented 3 years ago

Hey @LinnaViljami , If your schema is private, how are they going to get a JWT token?

pipedex commented 3 years ago

Hi , @LinnaViljami . You can divide your schemas in public and private endpoints. So, you can have one endpoint to login schema for public without login_required and one endpoint schema for private querys and mutations.

url('ingreso-login', csrf_exempt(FileUploadGraphQLView.as_view(graphiql=True, schema=schema1.schema1))),
url('ingreso-general', login_required(csrf_exempt((FileUploadGraphQLView.as_view(graphiql=True)))))

It is working for my on django 1.11 project.

But now i am upgrading to django 3.2 and sorpresively , when i protect with login_required my private schema, i get the token from login schema but dont can continue with the private endpoint schema.

paullanzw commented 2 years ago

The login_required we need, should be provided by django-graphql-jwt, not django itself.

lanshunfang commented 2 years ago

Here is my solution:

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

class PrivateGraphQLView(GraphQLView):
    """Adds a login requirement to graphQL API access via main endpoint."""

    def dispatch(self, request, *args, **kwargs):
        print(request.body)
        body_str = request.body.decode('utf-8')
        match_is_graphiql_ui = re.search(
            '"operationName":"IntrospectionQuery"}$', 
            body_str
        )

        if match_is_graphiql_ui or len(body_str) == 0:
            # allow graphiql UI to load
            pass
        else:
            token = JSONWebTokenAuthentication().authenticate(request)
            if token is None:
                raise Exception('Authorization failed')
        return super().dispatch(request, *args, **kwargs)

urlpatterns = [
    # ...
    path("graphql", csrf_exempt(PrivateGraphQLView.as_view(graphiql=True))),
]
lanshunfang commented 2 years ago

@LinnaViljami ^^