{"id":1146,"date":"2020-10-20T20:46:09","date_gmt":"2020-10-20T20:46:09","guid":{"rendered":"http:\/\/10.0.0.14\/?p=1146"},"modified":"2021-09-07T23:31:43","modified_gmt":"2021-09-07T23:31:43","slug":"tdd-with-django-graphene-docker-part-5-jwt","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/10\/20\/tdd-with-django-graphene-docker-part-5-jwt\/","title":{"rendered":"TDD with Gatsby, Django &#038; Docker Part 1, Chapter 05 &#8212; Django JWT"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Authentication with GraphQL &amp; JWT<\/h1>\n\n\n\n<p>Django comes with Users built in so, first we build our user app.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Create Our First User<\/h4>\n\n\n\n<p>Create a new folder called \/server\/<strong>users<\/strong> at the root of server with a new file schema.py: <strong>\/todo-app-graphql\/server\/users\/schema.py<\/strong> <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmkdir server\/users\ntouch server\/user\/schema.py\n<\/pre><\/div>\n\n\n<p>In our new \/server\/user\/schema.py file add the following:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# \/todo-app-graphql\/server\/users\/schema.py\n\nfrom django.contrib.auth import get_user_model\n\nimport graphene\nfrom graphene_django import DjangoObjectType\n\nclass UserType(DjangoObjectType):\n  class Meta:\n    model = get_user_model()\n\nclass CreateUser(graphene.Mutation):\n  user = graphene.Field(UserType)\n\n  class Arguments:\n    username = graphene.String(required=True)\n    password = graphene.String(required=True)\n    email    = graphene.String(required=True)\n\n  def mutate(self, info, username, password, email):\n    user = get_user_model()(\n      username=username,\n      email=email,\n    )\n    user.set_password(password)\n    user.save()\n\n    return CreateUser(user=user)\n\nclass Mutation(graphene.ObjectType):\n  create_user = CreateUser.Field()\n<\/pre><\/div>\n\n\n<p>Like our previous mutation CreateTodo we a CreateUser passing in the username, password, and email for our new user.<\/p>\n\n\n\n<p>We are ready to update our <strong>todo-app-graphql\/server\/todo_proj\/schema.py<\/strong> file with our new user mutation:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [6,14,15]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/schema.py\n\nimport graphene\n\nimport todo_app.schema\nimport users.schema\n\nclass Query(\n  todo_app.schema.Query,\n  graphene.ObjectType\n):\n  pass\n\nclass Mutation(users.schema.Mutation, todo_app.schema.Mutation, graphene.ObjectType):\n  pass\n\nschema = graphene.Schema(query=Query, mutation=Mutation)\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">Create a New User with GraphiQL<\/h4>\n\n\n\n<p>Add the following to GraphiQL at http:\/\/localhost:5555\/graphql:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmutation {\n  createUser(\n    username: &quot;ron&quot;,\n    email: &quot;ron@example.com&quot;,\n    password: &quot;123456A!&quot;\n  ) {\n    user {\n    id,\n    username,\n    email\n    }\n  }\n}\n<\/pre><\/div>\n\n\n<p>&#8230; which should create our new user.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphql_new_user.png\"><img loading=\"lazy\" decoding=\"async\" width=\"929\" height=\"525\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphql_new_user.png\" alt=\"\" class=\"wp-image-502\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_new_user.png 929w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_new_user-300x170.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_new_user-768x434.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_new_user-600x339.png 600w\" sizes=\"auto, (max-width: 929px) 100vw, 929px\" \/><\/a><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Query the Users<\/h4>\n\n\n\n<p>Next we update the <strong>server\/users\/schema.py<\/strong> file with a query, (see highlighted lines), to list all our users in the database.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [33,34,35,36,37]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/users\/schema.py\n\n\nfrom django.contrib.auth import get_user_model\nimport graphene\nfrom graphene_django import DjangoObjectType\n\nclass UserType(DjangoObjectType):\n  class Meta:\n    model = get_user_model()\n\nclass CreateUser(graphene.Mutation):\n  user = graphene.Field(UserType)\n\n  class Arguments:\n    username = graphene.String(required=True)\n    password = graphene.String(required=True)\n    email    = graphene.String(required=True)\n\n  def mutate(self, info, username, password, email):\n    user = get_user_model() (\n      username=username, \n      email = email,\n    )\n    user.set_password(password)\n    user.save()\n\n    return CreateUser(user=user)\n\nclass Mutation(graphene.ObjectType):\n  create_user = CreateUser.Field()\n\nclass Query(graphene.ObjectType):\n  users = graphene.List(UserType)\n\n  def resolve_users(self, info):\n    return get_user_model().objects.all()\n<\/pre><\/div>\n\n\n<p>Update the main query class with our new users query:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [5]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/schema.py\n\n# ... code\nclass Query(\n  users.schema.Query,\n  todo_app.schema.Query,\n  graphene.ObjectType\n):\n  pass\n<\/pre><\/div>\n\n\n<p>We can now add the following query to GraphiQL to query all our users in the database:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nquery {\n  users{\n    id\n    username\n    email\n  }\n}\n<\/pre><\/div>\n\n\n<p>&#8230; and voila! Our list of users.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphql_user_list.png\"><img loading=\"lazy\" decoding=\"async\" width=\"929\" height=\"526\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphql_user_list.png\" alt=\"\" class=\"wp-image-507\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_user_list.png 929w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_user_list-300x170.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_user_list-768x435.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphql_user_list-600x340.png 600w\" sizes=\"auto, (max-width: 929px) 100vw, 929px\" \/><\/a><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Authenticating Our Users<\/h4>\n\n\n\n<p>Most of the web apps today are&nbsp;<em>stateless<\/em> so, we will use the&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/flavors\/django-graphql-jwt\" target=\"_blank\">django-graphql-jwt<\/a>&nbsp;library to implement&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/jwt.io\/\" target=\"_blank\">JWT Tokens<\/a>&nbsp;in Graphene.<\/p>\n\n\n\n<p>When a User signs up or logs in, a token will be returned: a string of data that identifies the User. This token must be sent by the User in the HTTP Authorization header with&nbsp;<em>every request<\/em>&nbsp;when authentication is needed. If you want to know more about Django JWT checkout <a href=\"https:\/\/django-graphql-jwt.domake.io\/en\/latest\/\">the official docs<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Configuring django-graphql-jwt<\/h4>\n\n\n\n<p>If you cut and paste all the requirements into the requirements.txt file from part one of this tutorial, you already have django-graphql-jwt installed. If not you can install with the following<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server pip install django-graphql-jwt\n<\/pre><\/div>\n\n\n<p>To configure add the following to the MIDDLEWARE variable the todo-app-graphql\/server\/todo_proj\/settings.py file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [12,17,18,19]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/settings.py\n\n# ... code\n\nMIDDLEWARE = &#x5B;\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n]\n\nGRAPHENE = {\n  'SCHEMA': 'todo_proj.schema.schema',\n  'MIDDLEWARE': &#x5B;\n    'graphql_jwt.middleware.JSONWebTokenMiddleware',\n   ],\n}\n<\/pre><\/div>\n\n\n<p>The entire server\/todo_proj\/settings.py should look like this now with highlighted lines of updates:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [55,142,143,144]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# server\/todo_proj\/settings.py\n\n&quot;&quot;&quot;\nDjango settings for todo_proj project.\n\nGenerated by 'django-admin startproject' using Django 3.2.5.\n\nFor more information on this file, see\nhttps:\/\/docs.djangoproject.com\/en\/3.2\/topics\/settings\/\n\nFor the full list of settings and their values, see\nhttps:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/\n&quot;&quot;&quot;\n\nfrom pathlib import Path\n\n# Build paths inside the project like this: BASE_DIR \/ 'subdir'.\nBASE_DIR = Path(__file__).resolve().parent.parent\n\n\n# Quick-start development settings - unsuitable for production\n# See https:\/\/docs.djangoproject.com\/en\/3.2\/howto\/deployment\/checklist\/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = 'django-insecure-z6a#15u#4bw092uhpnvhbc_h!p$!&amp;amp;&amp;amp;g&amp;amp;qvb5dq3vj7kj3@#%bg'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nALLOWED_HOSTS = &#x5B;]\n\n\n# Application definition\n\nINSTALLED_APPS = &#x5B;\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'todo_app',\n    'mptt',\n    'graphene_django',\n]\n\nMIDDLEWARE = &#x5B;\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n]\n\nROOT_URLCONF = 'todo_proj.urls'\n\nTEMPLATES = &#x5B;\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': &#x5B;],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': &#x5B;\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n            ],\n        },\n    },\n]\n\nWSGI_APPLICATION = 'todo_proj.wsgi.application'\n\n\n# Database\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#databases\n\nimport os\n\nDATABASES = { \n'default': \n  { \n    'ENGINE': 'django.db.backends.postgresql', \n    'NAME': os.environ.get('DATABASE_NAME'), \n    'USER': os.environ.get('DATABASE_USER'), \n    'PASSWORD': os.environ.get('DATABASE_PASSWORD'),    \n    'HOST': os.environ.get('DATABASE_HOST'), \n    'PORT': os.environ.get('DATABASE_PORT'), \n  } \n}\n\n\n# Password validation\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#auth-password-validators\n\nAUTH_PASSWORD_VALIDATORS = &#x5B;\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n\n\n# Internationalization\n# https:\/\/docs.djangoproject.com\/en\/3.2\/topics\/i18n\/\n\nLANGUAGE_CODE = 'en-us'\n\nTIME_ZONE = 'UTC'\n\nUSE_I18N = True\n\nUSE_L10N = True\n\nUSE_TZ = True\n\n\n# Static files (CSS, JavaScript, Images)\n# https:\/\/docs.djangoproject.com\/en\/3.2\/howto\/static-files\/\n\nSTATIC_URL = '\/static\/'\n\n# Default primary key field type\n# https:\/\/docs.djangoproject.com\/en\/3.2\/ref\/settings\/#default-auto-field\n\nDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'\n\nGRAPHENE = {\n  'SCHEMA': 'todo_proj.schema.schema',\n  'MIDDLEWARE': &#x5B;\n    'graphql_jwt.middleware.JSONWebTokenMiddleware',\n   ],\n}\n\n<\/pre><\/div>\n\n\n<p>Also in the todo-app-graphql\/server\/todo_proj\/settings.py file add the AUTHENTICATION_BACKENDS setting:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/settings.py\n\n# ... code\n\nAUTHENTICATION_BACKENDS = &#x5B;\n  'graphql_jwt.backends.JSONWebTokenBackend',\n  'django.contrib.auth.backends.ModelBackend',\n]\n<\/pre><\/div>\n\n\n<p>In the todo-app-graphql\/server\/todo_proj\/schema.py file add:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/schema.py\n\nimport graphene\nimport graphql_jwt\n\n# ...code\n<\/pre><\/div>\n\n\n<p>Also in the todo-app-graphql\/server\/todo_proj\/schema.py file change the Mutation class by replacing &#8220;pass&#8221; with the following variables:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# todo-app-graphql\/server\/todo_proj\/schema.py\n\n# ... code\n\nclass Mutation(\n  users.schema.Mutation,\n  todo_app.schema.Mutation,\n  graphene.ObjectType\n):\n  token_auth = graphql_jwt.ObtainJSONWebToken.Field()\n  verify_token = graphql_jwt.Verify.Field()\n  refresh_token = graphql_jwt.Refresh.Field()\n\n# ... code\n<\/pre><\/div>\n\n\n<p>Your todo-app-graphql\/server\/todo_proj\/schema.py should look like this now. Note the highlighted lines for the updated code.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [2,15,16,17]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nimport graphene\nimport graphql_jwt\n \nimport todo_app.schema\nimport users.schema\n \nclass Query(\n  users.schema.Query,\n  todo_app.schema.Query,\n  graphene.ObjectType\n):\n  pass\n \nclass Mutation(users.schema.Mutation, todo_app.schema.Mutation, graphene.ObjectType):\n  token_auth = graphql_jwt.ObtainJSONWebToken.Field()\n  verify_token = graphql_jwt.Verify.Field()\n  refresh_token = graphql_jwt.Refresh.Field()\n \nschema = graphene.Schema(query=Query, mutation=Mutation)\n<\/pre><\/div>\n\n\n<p>Since we updated our settings.py file we need to reboot:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose down\ndocker-compose up -d\n<\/pre><\/div>\n\n\n<p>We have three new Mutations. The first, token_auth uses the User with its username and password to obtain its JSON Web token.<\/p>\n\n\n\n<p>Add the following mutation into graphiql:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmutation {\n  tokenAuth(username: &quot;ron&quot;, password: &quot;123456A!&quot;) {\n    token\n  }\n}\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"520\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-1024x520.jpg\" alt=\"\" class=\"wp-image-522\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-1024x520.jpg 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-300x152.jpg 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-768x390.jpg 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-600x305.jpg 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth-945x480.jpg 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_tokenauth.jpg 1038w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Using the returned token we will verify that the provided token is indeed for the user we requested.<\/p>\n\n\n\n<p>The token that is returned for you will be different from mine in the example below so you will need to use your token below.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmutation {\n  verifyToken(token:   &quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InJvbiIsImV4cCI6MTYwMTg2NTU4Nywib3JpZ19pYXQiOjE2MDE4NjUyODd9.5sakOlY_sf3MJIEq_FXNtJexalgNi7VIejUTuo_Bvok&quot;) \n  {\n  payload\n  }\n}\n<\/pre><\/div>\n\n\n<p>Voila! VerifyToken confirms that the token is valid which is passed in as an argument.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"521\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-1024x521.png\" alt=\"\" class=\"wp-image-523\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-1024x521.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-300x153.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-768x391.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-600x305.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken-945x481.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_verifyToken.png 1038w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>RefreshToken is used to obtain a new token within the renewed expiration time for non-expired tokens, if they are enabled to expire. We won&#8217;t be using this token.<\/p>\n\n\n\n<p>For more information check the <a href=\"https:\/\/github.com\/flavors\/django-graphql-jwt\" target=\"_blank\" rel=\"noreferrer noopener\">documentation here<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Test Our Authentication<\/h4>\n\n\n\n<p>In order to test to see if everything is working, we create a new Query called me. This query returns the User&#8217;s info if the user is logged in or an error.<\/p>\n\n\n\n<p>Update server\/users\/schema.py file with the following highlighted lines:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [34,40,41,42,43,44,45]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# server\/users\/schema.py\n\n\nfrom django.contrib.auth import get_user_model\nimport graphene\nfrom graphene_django import DjangoObjectType\n\nclass UserType(DjangoObjectType):\n  class Meta:\n    model = get_user_model()\n\nclass CreateUser(graphene.Mutation):\n  user = graphene.Field(UserType)\n\n  class Arguments:\n    username = graphene.String(required=True)\n    password = graphene.String(required=True)\n    email    = graphene.String(required=True)\n\n  def mutate(self, info, username, password, email):\n    user = get_user_model() (\n      username=username, \n      email = email,\n    )\n    user.set_password(password)\n    user.save()\n\n    return CreateUser(user=user)\n\nclass Mutation(graphene.ObjectType):\n  create_user = CreateUser.Field()\n\nclass Query(graphene.ObjectType):\n  me    = graphene.Field(UserType)\n  users = graphene.List(UserType)\n\n  def resolve_users(self, info):\n    return get_user_model().objects.all()\n\n  def resolve_me(self, info):\n    user = info.context.user\n    if user.is_anonymous:\n      raise Exception('Not logged in!')\n\n    return user\n<\/pre><\/div>\n\n\n<p>Now we need to switch from GraphiQL UI to a another app called Insomnia which can accept custom HTTP headers. You can download <a rel=\"noreferrer noopener\" href=\"https:\/\/insomnia.rest\/download\" target=\"_blank\">Insomnia here<\/a>.<\/p>\n\n\n\n<p>Using Insomnia let&#8217;s get a new token for our user:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmutation {\n  tokenAuth(username: &quot;ron&quot;, password: &quot;123456A!&quot;) {\n    token\n  }\n}\n<\/pre><\/div>\n\n\n<p>&#8230; which gives us this.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"548\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-1024x548.png\" alt=\"\" class=\"wp-image-821\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-1024x548.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-300x161.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-768x411.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-600x321.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2-945x506.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2-2.png 1250w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Add the token under the Header tab on Insomnia, add the AUTHORIZATION HTTP header with our token prefixed with the word jwt.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"548\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-1024x548.png\" alt=\"\" class=\"wp-image-820\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-1024x548.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-300x161.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-768x411.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-600x321.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header-945x506.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/08\/insomnia_tokenAuth_2_header.png 1250w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Using our new token we can query the identity of our user that we just logged in.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nquery {\n  me {\n    id\n    username\n  }\n}\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/insomnia_me_query.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"475\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/insomnia_me_query-1024x475.png\" alt=\"\" class=\"wp-image-529\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query-1024x475.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query-300x139.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query-768x357.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query-600x279.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query-945x439.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/insomnia_me_query.png 1038w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Now we can create users and login with them.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Integrating Todo Tasks and Users<\/h4>\n\n\n\n<p>Ok, where are we? We can login and access via Insomnia who is logged in. Excellent. What&#8217;s next? We don&#8217;t want anonymous users creating and editing todo tasks. Only logged in users can have that privilege so we need to link our users to todo tasks.<\/p>\n\n\n\n<p>We need update our todo model, todo-app-graphql\/todo_app\/models.py:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># todo-app-graphql\/todo_app\/models.py\n\nfrom django.conf import settings\n# ... code<\/pre>\n\n\n\n<p>Add a posted_by field:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># todo-app-graphql\/todo_app\/models.py\n\nfrom django.conf import settings\n# ... code\n\nclass Todo(models.Model):\n  title = models.CharField(max_length=5000)\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title', default='')\n  category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True, related_name='todos')\n  project = models.ForeignKey('Project', related_name='todo_project', on_delete=models.CASCADE, null=True, blank=True)\n  is_completed = models.BooleanField(default=False)\n  <span style=\"color: red; font-weight: bold\">posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)<\/span>\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\"))\n  due_date = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\"))\n\n  def __str__(self):\n    return self.title<\/pre>\n\n\n\n<p>Using docker-compose run makemigrations and migrate to update our database:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n# Stop server\ndocker-compose down\n# Start server so new migrations take effect\ndocker-compose up -d\n\n# Migrations\ndocker-compose run server .\/manage.py makemigrations\ndocker-compose run server .\/manage.py migrate\n<\/pre><\/div>\n\n\n<p>In CreateTodo mutation, todo-app-graphql\/todo_app\/schema.py, return the User in the newly created field:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># todo-app-graphql\/todo_app\/schema.py\n\n# ...code\n<span style=\"color: red; font-weight: bold\">from users.schema import UserType<\/span>\n\n# ...code\n# Update CreateTodo mutation\nclass CreateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  is_completed = graphene.Boolean()\n  slug = graphene.String()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  <span style=\"color: red; font-weight: bold\">posted_by = graphene.Field(UserType)<\/span>\n\n  class Arguments:\n    title = graphene.String()\n    task = graphene.String()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, title, task, projId=None, catId=None):\n    <span style=\"color: red; font-weight: bold\">user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to create new todos!')<\/span>\n\n    if projSlug:\n      proj = Project.objects.filter(slug=projSlug).first()\n    else:\n      proj = None\n\n    if catSlug:\n      cat = Category.objects.get(slug=catSlug)\n    else:\n      cat = None\n      \n    todo = Todo(\n      title=title, \n      task=task, \n      project=proj, \n      category=cat,\n      <span style=\"color: red; font-weight: bold\">posted_by=user,<\/span>\n    )\n    todo.save()\n\n    return CreateTodo(\n      id=todo.id,\n      title=todo.title,\n      task=todo.task,\n      slug=todo.slug,\n      project=todo.project,\n      category=todo.category,\n      <span style=\"color: red; font-weight: bold\">posted_by=todo.posted_by,<\/span>\n    )<\/pre>\n\n\n\n<p>Now we can test by sending a mutation to the server via GraphiQL, http:\/\/localhost:5555\/graphql:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mutation {\n  createTodo(\n    title: \"My Todo\",\n    task: \"Do this task\u2026\",\n    projId: 1,\n    catId: 1\n  ) {\n      id\n      title\n      task\n      postedBy {\n        id\n        username\n        email\n    }\n    project {\n      id\n      name\n      slug\n    }\n    category {\n      id\n      name\n    }\n  }\n}<\/pre>\n\n\n\n<p>But wait we get an error!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"422\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-1024x422.jpg\" alt=\"\" class=\"wp-image-540\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-1024x422.jpg 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-300x124.jpg 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-768x317.jpg 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-1536x633.jpg 1536w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-2048x844.jpg 2048w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-600x247.jpg 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/07\/graphiql_ui_not_logged_in_error-945x389.jpg 945w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>If you didn&#8217;t get an error and you were successful in creating a todo, you may be logged into the django backend, (<a href=\"http:\/\/localhost:5555\/admin\">http:\/\/localhost:5555\/admin<\/a>), using the same browser.<\/p>\n\n\n\n<p>Our create todo mutation is now password protected. In order to create a new task via GraphQL we need to use Insomnia which sends our user token in the header:<\/p>\n\n\n\n<p>Note that since we are referencing projId and catId in the createTodo arguments each with id 1 so, confirm that you have a project and a category each with id = 1 in your db if not adjust the id parameter accordingly.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/11\/insomnia_createTodo.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"646\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-1024x646.png\" alt=\"\" class=\"wp-image-1226\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-1024x646.png 1024w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-300x189.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-768x485.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-600x379.png 600w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo-945x596.png 945w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/11\/insomnia_createTodo.png 1109w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Next we need to update \/server\/todo_app\/<strong>schema.py<\/strong> file by adding user authentication for update todo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/schema.py\n\n# code...\n\nclass UpdateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  is_completed = graphene.Boolean()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  slug = graphene.String()\n  <span style=\"color: red; font-weight: bold;\">posted_by = graphene.Field(UserType)<\/span>\n\n  class Arguments:\n    id = graphene.Int()\n    title = graphene.String()\n    task = graphene.String()\n    is_completed = graphene.Boolean()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, id, title=None, task=None, is_completed=None, projId=None, catId=None):\n\n    <span style=\"color: red; font-weight: bold;\">user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')<\/span>\n\n    todo = Todo.objects.get(pk=id)\n    if projId:\n      project = Project.objects.get(pk=projId)\n      if not project:\n        raise Exception('There is no project with this id.')\n\n    if catId:\n      category = Category.objects.get(pk=catId)\n      if not category:\n        raise Exception('No category with this id exists.')\n\n    if todo:\n      if title:\n        todo.title = title\n      if task:\n        todo.task = task\n      if is_completed:\n        todo.is_completed = is_completed\n      if projId:\n        todo.project = project\n      if catId:\n        todo.category = category\n\n      todo.save()\n\n      return UpdateTodo(\n        id = todo.id,\n        title = todo.title,\n        task = todo.task,\n        is_completed = todo.is_completed,\n        project = todo.project,\n        category = todo.category,\n        slug = todo.slug,\n        <span style=\"color: red; font-weight: bold;\">posted_by=todo.posted_by,<\/span>\n      )\n    else:\n      raise Exception('There is no todo with this id.')\n\n# code ...\nclass Mutation(graphene.ObjectType):\n  # code...\n  update_todo = UpdateTodo.Field()\n<\/pre>\n\n\n\n<p>Using Insomnia GraphQL we can update our Todo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">mutation{\n  updateTodo(\n    id: 1,\n    title: \"todo1 updated\"\n  ) {\n    id\n    title\n    postedBy{\n      id\n      username\n      email\n    }\n  }\n}<\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/10\/updateTodo.png\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"509\" src=\"http:\/\/10.0.0.14\/wp-content\/uploads\/2020\/10\/updateTodo.png\" alt=\"\" class=\"wp-image-1042\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/10\/updateTodo.png 800w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/10\/updateTodo-300x191.png 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/10\/updateTodo-768x489.png 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2020\/10\/updateTodo-600x382.png 600w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a><\/figure>\n\n\n\n<p>So now both create and updating todos are protected by user authentication. That&#8217;s fine allowing any authenticated user the permission for creating a new todo, but we only want the user who originally created the todo to be able to edit it. For that we need to add an additional layer of authentication testing that the logged in user is indeed the same as the user who created the todo.<\/p>\n\n\n\n<p>We can do this by comparing current user id and posted by id, e.g. user.id != posted_by.id. Lets add this to our UpdateTodo mutation below:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/schema.py\n\n# code...\n\nclass UpdateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  is_completed = graphene.Boolean()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  slug = graphene.String()\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    id = graphene.Int()\n    title = graphene.String()\n    task = graphene.String()\n    is_completed = graphene.Boolean()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, id, title=None, task=None, is_completed=None, projId=None, catId=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    todo = Todo.objects.get(pk=id)\n\n    <span style=\"color: red; font-weight: bold\"># test if logged in user is the same as posted_by\n    if todo.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')<\/span>\n\n    if projId:\n      project = Project.objects.get(pk=projId)\n      if not project:\n        raise Exception('There is no project with this id.')\n\n    if catId:\n      category = Category.objects.get(pk=catId)\n      if not category:\n        raise Exception('No category with this id exists.')\n\n    if todo:\n      if title:\n        todo.title = title\n      if task:\n        todo.task = task\n      if is_completed:\n        todo.is_completed = is_completed\n      if projId:\n        todo.project = project\n      if catId:\n        todo.category = category\n\n      todo.save()\n\n      return UpdateTodo(\n        id = todo.id,\n        title = todo.title,\n        task = todo.task,\n        is_completed = todo.is_completed,\n        project = todo.project,\n        category = todo.category,\n        slug = todo.slug,\n        posted_by=todo.posted_by,\n      )\n    else:\n      raise Exception('There is no todo with this id.')\n<\/pre>\n\n\n\n<p>Now only the user who created the todo can edit it. Try it in Insomnia.<\/p>\n\n\n\n<p>Next we need to add user authentication for all Crud operations which the updated todo app schema and model addresses below.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Updated for All Mutations for Todo App Schema<\/h4>\n\n\n\n<p>Below is updated schema including:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Delete Todo<\/li><li>Create Category<\/li><li>Update Category<\/li><li>Delete Category<\/li><li>Create Project<\/li><li>Update Project<\/li><li>Delete Project<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\"># \/server\/todo_app\/<strong>schema.py<\/strong>\n\nimport graphene\nfrom graphene import Schema\nfrom graphene_django import DjangoObjectType\nfrom graphql import GraphQLError\nfrom todo_app.models import Todo, Project, Category\nfrom users.schema import UserType\n\nclass TodoType(DjangoObjectType):\n  class Meta:\n    model = Todo\n\nclass ProjectType(DjangoObjectType):\n  class Meta:\n    model = Project\n\nclass CategoryType(DjangoObjectType):\n  class Meta:\n    model = Category\n\nclass Query(graphene.ObjectType):\n  \n  # todo queries\n  todos = graphene.List(TodoType)\n  def resolve_todos(self, info, **kwargs):\n    return Todo.objects.all()\n\n  todo = graphene.Field(\n    TodoType,\n    id=graphene.Int(),\n  ) \n  def resolve_todo(self, info, id):\n    return Todo.objects.get(pk=id)\n\n  # project queries\n  projects = graphene.List(ProjectType)\n  def resolve_projects(self, info, **kwargs):\n    return Project.objects.all()\n\n  project = graphene.Field(\n    ProjectType,\n    id=graphene.Int(),\n  )\n  def resolve_project(self, info, id):\n    return Project.objects.get(pk=id)\n\n  # category queries\n  categories = graphene.List(CategoryType)\n  def resolve_categories(self, info, **kwargs):\n    return Category.objects.all()\n\n  category = graphene.Field(\n    CategoryType,\n    id=graphene.Int(),\n  )\n  def resolve_category(self, info, id):\n    return Category.objects.get(pk=id)\n\n# Mutations\nclass CreateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  is_completed = graphene.Boolean()\n  slug = graphene.String()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    title = graphene.String()\n    task = graphene.String()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, title, task, projId=None, catId=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    if projId:\n      proj = Project.objects.get(pk=projId)\n    else:\n      proj = None\n\n    if catId:\n      cat = Category.objects.get(pk=catId)\n    else:\n      cat = None\n      \n    todo = Todo.objects.create(\n      title=title, \n      task=task, \n      project=proj, \n      category=cat,\n      posted_by=user,\n    )\n\n    return CreateTodo(\n      id=todo.id,\n      title=todo.title,\n      task=todo.task,\n      slug=todo.slug,\n      project=todo.project,\n      category=todo.category,\n      posted_by=todo.posted_by,\n    )\n\nclass UpdateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  slug = graphene.String()\n  is_completed = graphene.Boolean()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    id = graphene.Int()\n    title = graphene.String()\n    task = graphene.String()\n    is_completed = graphene.Boolean()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, id, title=None, task=None, is_completed=None, projId=None, catId=None):\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    todo = Todo.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if todo.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if projId:\n      project = Project.objects.get(pk=projId)\n      if not project:\n        raise Exception('There is no project with this id.')\n\n    if catId:\n      category = Category.objects.get(pk=catId)\n      if not category:\n        raise Exception('No category with this id exists.')\n\n    if todo:\n      if title:\n        todo.title = title\n      if task:\n        todo.task = task\n      if is_completed:\n        todo.is_completed = is_completed\n      if projId:\n        todo.project = project\n      if catId:\n        todo.category = category\n\n      todo.save()\n\n      return UpdateTodo(\n        id = todo.id,\n        title = todo.title,\n        task = todo.task,\n        slug = todo.slug,\n        is_completed = todo.is_completed,\n        project = todo.project,\n        category = todo.category,\n        posted_by=todo.posted_by,\n      )\n    else:\n      raise Exception('There is no todo with this id.')\n\nclass DeleteTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  ok = graphene.Boolean()\n\n  class Arguments:\n    id = graphene.Int()\n\n  def mutate(self, info, id=id):\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    todo = Todo.objects.get(pk=id)\n    \n    # test if logged in user is the same as posted_by\n    if todo.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if todo:\n      todo.delete()\n      return DeleteTodo(\n        id = todo.id,\n        title = todo.title,\n        ok = True\n      )\n\nclass UpdateTodo(graphene.Mutation):\n  id = graphene.Int()\n  title = graphene.String()\n  task = graphene.String()\n  is_completed = graphene.Boolean()\n  project = graphene.Field(ProjectType)\n  category = graphene.Field(CategoryType)\n  slug = graphene.String()\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    id = graphene.Int()\n    title = graphene.String()\n    task = graphene.String()\n    is_completed = graphene.Boolean()\n    projId = graphene.Int()\n    catId = graphene.Int()\n\n  def mutate(self, info, id, title=None, task=None, is_completed=None, projId=None, catId=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    todo = Todo.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if todo.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if projId:\n      project = Project.objects.get(pk=projId)\n      if not project:\n        raise Exception('There is no project with this id.')\n\n    if catId:\n      category = Category.objects.get(pk=catId)\n      if not category:\n        raise Exception('No category with this id exists.')\n\n    if todo:\n      if title:\n        todo.title = title\n      if task:\n        todo.task = task\n      if is_completed:\n        todo.is_completed = is_completed\n      if projId:\n        todo.project = project\n      if catId:\n        todo.category = category\n\n      todo.save()\n\n      return UpdateTodo(\n        id = todo.id,\n        title = todo.title,\n        task = todo.task,\n        is_completed = todo.is_completed,\n        project = todo.project,\n        category = todo.category,\n        slug = todo.slug,\n        posted_by=todo.posted_by,\n      )\n    else:\n      raise Exception('There is no todo with this id.')\n\nclass CreateProject(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  description = graphene.String()\n  slug = graphene.String()\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    name = graphene.String()\n    description = graphene.String()\n\n  def mutate(self, info, name, description=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    project = Project.objects.create(\n      name=name, \n      description=description,\n      posted_by=user,\n    )\n\n    return CreateProject(\n      id = project.id,\n      name = project.name,\n      description = project.description,\n      slug = project.slug,\n      posted_by = project.posted_by,\n    )\n\nclass DeleteProject(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  ok = graphene.Boolean()\n\n  class Arguments:\n    id = graphene.Int()\n\n  def mutate(self, info, id=id):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n    \n    project = Project.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if project.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if project:\n      project.delete()\n      return DeleteProject(\n        id = project.id,\n        name = project.name,\n        ok = True,\n      )\n\nclass UpdateProject(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  description = graphene.String()\n  slug = graphene.String()\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    id = graphene.Int()\n    name = graphene.String()\n    description = graphene.String()\n  \n  def mutate(self, info, id=id, name=None, description=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    project = Project.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if project.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if project:\n      if name:\n        project.name = name\n      if description:\n        project.description = description\n\n      project.save()\n\n      return UpdateProject(\n        id = project.id,\n        name = project.name,\n        description = project.description,\n        slug = project.slug, \n        posted_by = project.posted_by,\n      )\n    else:\n      raise Exception('There is no project with this id.')\n\nclass CreateCategory(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  slug = graphene.String()\n  parent = graphene.Field(CategoryType)\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    name = graphene.String()\n    parentId = graphene.Int()\n\n  def mutate(self, info, name, parentId=None):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    if parentId:\n      parent = Category.objects.get(pk=parentId)\n      category = Category.objects.create(\n        name=name, \n        parent=parent,\n        posted_by=user,\n      )\n    else:\n      category = Category.objects.create(\n        name=name,\n        posted_by=user,\n      )\n\n    return CreateCategory(\n      id=category.id,\n      name=category.name,\n      slug=category.slug,\n      parent=category.parent,\n      posted_by=category.posted_by,\n    )\n\nclass DeleteCategory(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  slug = graphene.String()\n  ok = graphene.Boolean()\n\n  class Arguments:\n    id = graphene.Int()\n\n  def mutate(self, info, id=id):\n\n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n\n    category = Category.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if category.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if category:\n      category.delete()\n      return DeleteCategory(\n        id = category.id,\n        name = category.name,\n        slug = category.slug,\n        ok = True\n      )\n\nclass UpdateCategory(graphene.Mutation):\n  id = graphene.Int()\n  name = graphene.String()\n  parent = graphene.Field(CategoryType)\n  slug = graphene.String()\n  posted_by = graphene.Field(UserType)\n\n  class Arguments:\n    id = graphene.Int()\n    name = graphene.String()\n    parentId = graphene.Int()\n\n  def mutate(self, info, id, name=None, parentId=None):\n    \n    user = info.context.user\n    if user.is_anonymous:\n      raise GraphQLError('You must be logged in to execute this mutation!')\n    \n    category = Category.objects.get(pk=id)\n\n    # test if logged in user is the same as posted_by\n    if category.posted_by.id != user.id:\n      raise GraphQLError('You do not have permission to execute this mutation!')\n\n    if parentId:\n      parent = Category.objects.get(pk=parentId)\n      if not parent:\n        raise Exception('No category with this id to be used as the updated parent.')\n\n\n    if category:\n      if name:\n        category.name = name\n      if parentId:\n        category.parent = parent\n      \n      category.save()\n\n      return UpdateCategory(\n        id = category.id,\n        name = category.name,\n        parent = category.parent,\n        slug = category.slug,\n        posted_by = category.posted_by,\n      )\n    else:\n      raise Exception('No category with this id.')      \n\n\n\nclass Mutation(graphene.ObjectType):\n  create_todo = CreateTodo.Field()\n  update_todo = UpdateTodo.Field()\n  delete_todo = DeleteTodo.Field()\n  create_project = CreateProject.Field()\n  delete_project = DeleteProject.Field()\n  update_project = UpdateProject.Field()\n  create_category = CreateCategory.Field()\n  delete_category = DeleteCategory.Field()\n  update_category = UpdateCategory.Field()\n\nschema = Schema(query=Query, mutation=Mutation)\n<\/pre>\n\n\n\n<p>Whew! That was a lot of work but, we only have to do this work once because everytime we run tests we will know definitively if there are any errors. Beats running all these mutations in GraphiQL or Insomnia <em>every<\/em> time we make a change.<\/p>\n\n\n\n<p>We will need to update server\/todo_app\/models.py by adding posted_by to Project and Category:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># server\/todo_app\/models.py\n\nfrom django.db import models\nfrom autoslug import AutoSlugField\nfrom django.utils import timezone\nfrom datetime import datetime\nfrom django.conf import settings\nfrom mptt.models import MPTTModel, TreeForeignKey\n\nclass Category(MPTTModel):\n  name = models.CharField(max_length=50, unique=True)\n  parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')\n  <span style=\"color: red; font-weight: bold;\">posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)<\/span>\n  slug = AutoSlugField(populate_from='name')\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False) \n  \n  created = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\")) \n\n  def __str__(self): \n    return self.name \n\n  class MPTTMeta: \n    order_insertion_by = ['name'] \n    unique_together = ('slug', 'parent') \n\n  class Meta: \n    verbose_name_plural = 'categories'\n\nclass Project(models.Model):\n  name = models.CharField(max_length=500)\n  description = models.TextField(max_length=2500, blank=True, null=True, help_text=\"Description of your project.\")\n  <span style=\"color: red; font-weight: bold;\">posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)<\/span>\n  slug = AutoSlugField(populate_from='name')\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False) \n  created = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\")) \n\n  def __str__(self): \n    return self.name \n\n  class Meta: \n    verbose_name_plural = 'projects'\n\n    \nclass Todo(models.Model):\n  title = models.CharField(max_length=5000)\n  task = models.TextField(blank=True)\n  slug = AutoSlugField(populate_from='title')\n  category = TreeForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True, related_name='todos')\n\n  project = models.ForeignKey('Project', on_delete=models.CASCADE, null=True, blank=True)\n  <span style=\"color: red; font-weight: bold;\">posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)<\/span>\n  is_completed = models.BooleanField(default=False)\n\n  modified = models.DateTimeField(auto_now=True, null=True, editable=False)\n  created = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\"))\n  due_date = models.DateField(default=timezone.now().strftime(\"%Y-%m-%d\"))\n\n  def __str__(self):\n    return self.title<\/pre>\n\n\n\n<p>Since we updated the model we need to run migrations:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose run server .\/manage.py makemigrations\ndocker-compose run server .\/manage.py migrate\n<\/pre><\/div>\n\n\n<p>We can now test all Crud operations on our app with GraphiQL.<\/p>\n\n\n\n<p>What about running pytest which is much faster. Ah, yes, if we were to run pytest now all our mutation tests would fail because our mutation tests are not adding the JWT token as we were doing with insomnia. That is our next step.<\/p>\n\n\n\n<p>Next chapter we dive into testing jwt mutations!<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-django-graphene-docker-part-4\">&lt; Previous:  Chapter 4<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"todo-app-with-gatsby-django-graphene-docker-pt-6\">Next: Chapter 6 &gt;<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Authentication with GraphQL &amp; JWT Django comes with Users built in so, first we build our user app. Create Our First User Create a new folder called \/server\/users at the root of server with a new file schema.py: \/todo-app-graphql\/server\/users\/schema.py In&#8230; <a class=\"more-link\" href=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/2020\/10\/20\/tdd-with-django-graphene-docker-part-5-jwt\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":312,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,20],"tags":[43,15,37,30,29],"class_list":["post-1146","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-django","category-featured-tutorial","tag-authentication","tag-django","tag-graphql","tag-jwt","tag-tdd"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1146","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1146"}],"version-history":[{"count":62,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1146\/revisions"}],"predecessor-version":[{"id":2506,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1146\/revisions\/2506"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/312"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}