Authenticating users in Graphql with Django session authentication

Quick notes about working with GraphQL and Django session authentication.

Authenticating users in Graphql with Django session authentication

We know that in JavaScript, cookies can travel over AJAX requests as long as the request comes from the same origin, and goes to the same origin. In other words, an AJAX request from https://www.pluto.com/ to https://www.pluto.com/api/ carries any cookie currently set in the browser, by sending them in a Cookie header.

What this means in a Django project for example is that if a user is authenticated, and a template happens to make an AJAX request to the same backend, authentication credentials are transmitted by default.

In Django, the authentication cookie stored in the browser is called sessionid by default.

In fact, by examining the headers of a WSGIRequest or ASGIRequest for an authenticated user in Django, we should be able to see something along these lines:

{ 'Cookie': 'sessionid=g9eflhxbeih1lgmslnybt5dn21zgk28t'; csrftoken=D3DtmfPKxriKMoy70eYikf8pUEVMTy3bDTczk8Ni0BNFVArAWg9oGat5V8PfKQW1 }

Such a request means that the user issuing the request is indeed authenticated. Here's the crazy idea: if you use GraphQL under Django session auth umbrella, you can use validate the sessionid cookie in the resolver itself.

Here's how.

Validating sessionid in a GraphQL resolver

Consider the following Ariadne GraphQL resolver:

@mutation.field("replyUpdate")
def reply_update(_obj: Any, info: GraphQLResolveInfo, reply):
    """Resolver for reply update."""

    request: ASGIRequest = info.context["request"]
    
    # do work ...

The second parameter of the resolver is info, which has access to the current request, be it an WSGIRequest or ASGIRequest (the same holds true for Strawberry).

How do we know that the user is authenticated in such a setup?

For some projects, it's not crazy to have the JavaScript frontend served by a Django template view, it could even be an SPA, or have a sprinkle of JavaScript in a template, making requests to the Django backend (Decoupled Django covers both approaches).

If you're running GraphQL in your Django project, and some of your JavaScript needs to make GraphQL requests to the backend, you can validate the session with get_user() from django.contrib.auth, as in the following example where I check that the user is both authenticated, and a staff member:

@mutation.field("replyUpdate")
def reply_update(_obj: Any, info: GraphQLResolveInfo, reply):
    """Resolver for reply update."""

    request: ASGIRequest = info.context["request"]
    user = get_user(request)

    if user.is_authenticated and user.is_staff:
        # do work, return the reply

    return {"error": {"detail": "Authentication credentials were not provided."}}

If the user is not authenticated, I return a meaningful error to the client.

To give you some more context, here's also the JavaScript snippet in charge of sending out the GraphQL mutation:

form.addEventListener('submit', function (event) {
    event.preventDefault();
    const formData = new FormData(event.target);

    const body = {
        variables: {
            reply: {
                message: formData.get("edit-reply"),
                id: replyId
            }
        },
        query: `mutation replyUpdate($reply: ReplyInput!){
                    replyUpdate(reply: $reply) {
                        error { detail },
                        reply { message }
                    }
                }`
        }

    fetch("{% url "support:graphql" %}", {
        method: 'POST',
        body: JSON.stringify(body),
        headers: {
            "Content-Type": "application/json"
        }
    })
    .then( /* handle the response */ )
    .then( /* handle GraphQL response*/ );
});

Also, from what I'm seeing, get_user() works fine under ASGI without any trouble.

Thanks for reading!

Resources

Valentino Gagliardi

Hi! I'm Valentino! I'm a freelance consultant with a wealth of experience in the IT industry. I spent the last years as a frontend consultant, providing advice and help, coaching and training on JavaScript, testing, and software development. Let's get in touch!

More from the blog: