Building GraphQL API with Python & Django Part #6: JWT Authentication

In this post we will setup the JWT token authentication in Django GraphQL API to authenticate the users



In this post, we are going to see how to protect particular GraphQL API using JSON web tokens that allow the users to authenticate with jwt tokens.

Django has a built-in concept of Users, so we need to send data to the server through a mutation to create User.
we will be;
  • Creating the Django Users models to create a new user
  • We will add a mutation that allows obtaining the token with username/password
  • We will authenticate the users using the token in our resolvers

One of the most popular ways to enforce some kind of authorization in an application is through the use of JSON web tokens (JWT).

Users authenticate with a service and the service responds with a JWT to be used in every future request so that way the password is kept safe. The service can then validate the JWT to make sure it is correct and not expired.
So let's get started!
. . .

Creating a User App

Django has a built-in concept of Users, so we need to send data to the server through a mutation to create User. To do so, we need to send data to the server through a mutation.

We need to add one more App in our Django application that deals with the user related things such as creating the users, creating the token for users and validation of token, so let's create a users app:
python manage.py startapp users

It will create a new application named as users into the App.

We will create and new file schema.py in our users app that defines the Schema for our Users app

#1
class CreateUser(graphene.Mutation):
user = graphene.Field(UserType)
#2
class Arguments:
username = graphene.String(required=True)
password = graphene.String(required=True)
email = graphene.String(required=True)
#3
def mutate(self, info, username, password, email):
user = get_user_model()(
username=username,
email=email,
)
user.set_password(password)
user.save()

return CreateUser(user=user)

#4
class Query(graphene.ObjectType):
users = graphene.List(UserType)

def resolve_users(self, info):
return get_user_model().objects.all()
#5
class Mutation(graphene.ObjectType):
create_user = CreateUser.Field()
Let us walk through the comments to understand the code:
  1. We have defined CreateUser Mutation that creates the new User
  2. CreateUser mutation accepts the username, email, and password as arguments
  3. We have defined the resolver function for CreateUser
  4. We have defined the root query for Users app, it has users query that returns all the users in the database
  5. We have defined the root mutation for users app, it has create_user mutation that allows creating a new user
When sending data to the server using mutation, we will send username, password and email. The server will return the created User object with all the information, where the client can ask the fields it wants.

Before executing it, we need to put the new mutation in the root schema.py file.

import swapi.schema
import users.schema

class Query(users.schema.Query, swapi.schema.Query, graphene.ObjectType):
pass

class Mutation(users.schema.Mutation,swapi.schema.Mutation, graphene.ObjectType):
pass

schema = graphene.Schema(query=Query, mutation=Mutation)

Now we have included the users app schema into our root schema, Execute the following mutation in the GraphiQL interface:
mutation {
createUser (
username: "testuser",
email: "test@gmail.com",
password: "testuser"
) {
user {
id
username
email
}
}
}

In response, you already can see the new user has been created.

To list all the users we can run the following query in the GraphiQL interface:
{
users {
id
username
email
}
}

In response, you already can see the list of users available in the database.
. . .

JWT Authentication

The concept of authentication and authorization is enabled by default in Django using sessions. Since most of the web apps today are stateless, we are going to use the django-graphql-jwt library to implement JWT Tokens in Graphene

A piece of data that identifies the User is sent to a User when they sign up or log in. The same token should be sent by the User in HTTP Authorization header with the request when authentication is required.

We will configure django-graphql-jwt by adding a new middleware in settings.py file.

Add AuthenticationMiddleware middleware to your MIDDLEWARE settings:
MIDDLEWARE = [ ... 'django.contrib.auth.middleware.AuthenticationMiddleware', ... ]

Add JSONWebTokenMiddleware middleware to your GRAPHENE settings:
GRAPHENE = { 'SCHEMA': 'mysite.myschema.schema', 'MIDDLEWARE': [ 'graphql_jwt.middleware.JSONWebTokenMiddleware', ], }

Add JSONWebTokenBackend backend to your AUTHENTICATION_BACKENDS:
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
]

Next, we will import the library and update the Mutation class in our users app to have the following variables in schema.py file.
class Mutation(graphene.ObjectType):
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.Verify.Field()
refresh_token = graphql_jwt.Refresh.Field()

TokenAuth is used to authenticate the User with its username and password to obtain the JSON Web token.
mutation {
tokenAuth(
username: "testuser",
password: "testuser"
) {
token
}
}

VerifyToken to confirm that the token is valid, passing it as an argument.

RefreshToken will generate a new token for non-expired tokens within the renewed expiration time.
. . .

Testing the Authentication

To test if our authentication is working, we need to update our resolvers functions, user info is available in resolvers context, we can use it to determine if the user is authenticated or not.

Lets secure the query that allows getting a list of all Starwars Humans & allows to get Human character by Id as follows:
class Query(graphene.ObjectType):
... more queries
human = graphene.Field(
HumanType,
id=graphene.NonNull(graphene.Int)
)
def resolve_human(self, info, id):
if info.context.user.is_anonymous:
raise Exception('You are not authenticated!')
return resolver_human(id=id)

As you can see inside the context of resolver function we have access to the users info, if the user is not authenticated we can throw the authentication error. if the user is authenticated we will return the data
To test it out, we need to get a token using the tokenAuth Mutation and use it in our Query with the AUTHORIZATION HTTP header, using the JWT prefix

Inside the Headers tab of our GraphQL playground set the JWT token as follows
{
"Authorization": "JWT paste your actual token here"
}

To get all the Human characters we can run the following query in the GraphiQL interface with valid JWT token passed into the headers:
{
humans {
id
name
homePlanet
mass
height
birthYear
}
}

Awww yeah! You are now able to authenticate the users with JWT. Try to make the query without the HTTP header and an error message should appear.
. . .
In the next tutorial, we will add realtime subscription functionality into our Django application.



Never miss a post from Gufran Mirza, when you sign up for Ednsquare.