Authentication

In this section we'll see how we can implement authentication for our API. GraphQL doesn't dictate any way to implement authentication, so we can use any method we want. It can be a simple API key, a JWT token, a cookie based authentication or even basic auth.

We'll be using session based authentication in this section. We'll be leveraging django's authentication so we won't be needing to write much code, fortunately.

Mutations

This is a good time to talk about mutations. Mutations are the way we can perform write operations in GraphQL. We can create, update and delete data using mutations.

We'll be creating a mutation to login users. While login might seem like an operation that doesn't do any writes, it's actually a write operation. We're writing a session to the database. And we might also be updating the user with the last login time.

Additionally if we enable GraphQL queries via GET requests, we don't want users to be able to login via a GET request.

Creating the mutation

Mutations in strawberry are very similar to queries. The only difference is we use strawberry.mutation instead of strawberry.field when defining the mutation.

In our project we already have an api/authentication/mutation.py file, we can add our login mutation here.

As usual we also have a function in our data module that will allow us to login users. We'll be using this function in our mutation.

Let's see how we can implement the login mutation here:

import strawberry
from strawberry.types import Info

from api.views import Context
from users.authenticate import authenticate_and_login


@strawberry.type
class LoginPayload:
    ok: bool


@strawberry.type
class AuthenticationMutation:
    @strawberry.mutation
    async def login(
        self, info: Info[Context, None], email: str, password: str
    ) -> LoginPayload:
        request = info.context["request"]

        user = await authenticate_and_login(request, email=email, password=password)

        if user is not None:
            return LoginPayload(ok=True)

        return LoginPayload(ok=False)

This is very similar to what we have done so far with our queries. We have a LoginPayload type that contains a boolean field called ok. This field will be True if the login was successful and False otherwise.

Our login mutation takes an email and a password as arguments. But this time we also have an info argument. This argument is a special argument that contains information about the request. We can use this argument to access the request object and the user object. In this case info.context["request"] will be the Django HttpRequest object. We'll pass this to authenticate_and_login to login the user.

Let's test it

Before being able to test this we'll need to create a user, which we can do by running the following command:

python manage.py createsuperuser

once you have a user you can run the following query to login:

mutation {
  login(email: "your@email.com", password: "password") {
    ok
  }
}

If you get ok: true then you have successfully logged in. The session cookie will be set in your browser.