JSON Web Token for Django GraphQL
django-graphql-jwt is a Python library that provides JSON Web Token (JWT) authentication for Django GraphQL applications. It integrates seamlessly with graphene-django and Django's authentication system, offering mutations for obtaining, refreshing, and verifying tokens, as well as decorators and mixins for protecting GraphQL views and fields. It is currently at version 0.4.0 and typically releases updates as needed, often tied to major versions of Django, Graphene-Django, or PyJWT.
Common errors
-
ModuleNotFoundError: No module named 'graphql_jwt'
cause `django-graphql-jwt` has not been installed or is not available in the current Python environment.fixRun `pip install django-graphql-jwt` in your project's virtual environment. -
django.core.exceptions.ImproperlyConfigured: AUTHENTICATION_BACKENDS must contain 'graphql_jwt.backends.JSONWebTokenBackend'.
cause The required JWT authentication backend is missing from Django's `AUTHENTICATION_BACKENDS` setting.fixAdd `'graphql_jwt.backends.JSONWebTokenBackend'` to the `AUTHENTICATION_BACKENDS` list in your `settings.py`. -
Cannot query field "tokenAuth" on type "Mutation".
cause The GraphQL `Mutation` type in your schema does not include the `tokenAuth` field (or `verifyToken`, `refreshToken`).fixEnsure your `Mutation` class in `schema.py` explicitly includes `token_auth = ObtainJSONWebToken.Field()` (and similar for `verify_token` and `refresh_token`). -
graphql_jwt.exceptions.JSONWebTokenError: Signature has expired
cause The provided JWT token has exceeded its expiration time, as defined by `JWT_EXPIRATION_DELTA` in your `GRAPHQL_JWT` settings.fixObtain a new token using the `tokenAuth` mutation or use the `refreshToken` mutation with a valid refresh token. If tokens expire too quickly for your use case, adjust `JWT_EXPIRATION_DELTA` in `settings.py` (e.g., `datetime.timedelta(hours=1)`).
Warnings
- breaking As of version 0.4.0, JWT token verification errors are now correctly propagated as `graphql_jwt.exceptions.JSONWebTokenError` exceptions, rather than being silently suppressed or returning `None` in some contexts. This changes error handling behavior.
- gotcha For `django-graphql-jwt` to function correctly, `graphql_jwt.backends.JSONWebTokenBackend` MUST be included in your Django project's `AUTHENTICATION_BACKENDS` setting. Without it, Django's authentication system will not recognize the JWT backend.
- gotcha To ensure that the authenticated user information from a JWT is available in `info.context.user` within your GraphQL resolvers, you must include `graphql_jwt.middleware.JSONWebTokenMiddleware` in your Django project's `MIDDLEWARE` setting.
- deprecated The `refresh_token` field on the `tokenAuth` mutation is deprecated as of version 0.4.0. Users should migrate to using the standalone `RefreshTokenMutation` for token refreshing.
Install
-
pip install django-graphql-jwt
Imports
- login_required
from graphene_jwt.decorators import login_required
from graphql_jwt.decorators import login_required
- JSONWebTokenMixin
from graphql_jwt.mixins import JSONWebTokenMixin
- ObtainJSONWebToken
from graphql_jwt.mutations import JSONWebTokenMutation
from graphql_jwt.mutations import ObtainJSONWebToken
- VerifyToken
from graphql_jwt.mutations import VerifyToken
- RefreshToken
from graphql_jwt.mutations import RefreshToken
- jwt_settings
from graphql_jwt.settings import jwt_settings
- JSONWebTokenBackend
graphql_jwt.backends.JSONWebTokenBackend
- JSONWebTokenMiddleware
graphql_jwt.middleware.JSONWebTokenMiddleware
Quickstart
import graphene
import graphql_jwt
from django.conf import settings
from django.core.management import call_command
from django.contrib.auth import get_user_model
# Minimal Django settings for a runnable example
if not settings.configured:
settings.configure(
SECRET_KEY='a-very-secret-key-for-testing',
DEBUG=True,
ALLOWED_HOSTS=['*'],
INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django',
'graphql_jwt',
],
AUTHENTICATION_BACKENDS=[
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
],
MIDDLEWARE=[
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
ROOT_URLCONF=__name__, # Simplistic for example
TEMPLATES=[{ # Required for admin to work minimally
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 'APP_DIRS': True,
'OPTIONS': {'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
]}},
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
GRAPHQL_JWT={ # Example custom settings
'JWT_VERIFY_EXPIRATION': True,
'JWT_EXPIRATION_DELTA': 'datetime.timedelta(minutes=5)',
'JWT_REFRESH_EXPIRATION_DELTA': 'datetime.timedelta(days=7)',
}
)
# Apply migrations for auth models
call_command('migrate', verbosity=0, interactive=False)
# Create a dummy user for the example
User = get_user_model()
try:
User.objects.get(username='testuser')
except User.DoesNotExist:
user = User.objects.create_user(username='testuser', email='test@example.com', password='password123')
# Define your GraphQL schema
class Query(graphene.ObjectType):
hello = graphene.String(name=graphene.String(default_value="World"))
def resolve_hello(self, info, name):
return f"Hello {name}!"
# Custom mutation for obtaining token with user info
class ObtainJSONWebToken(graphql_jwt.JSONWebTokenMutation):
user = graphene.Field(User.__class__)
@classmethod
def resolve_mutant(cls, root, info, **kwargs):
return cls(user=info.context.user)
# Custom mutation for refreshing token with user info
class RefreshToken(graphql_jwt.RefreshTokenMutation):
user = graphene.Field(User.__class__)
@classmethod
def resolve_mutant(cls, root, info, **kwargs):
return cls(user=info.context.user)
# Custom mutation for verifying token with user info
class VerifyToken(graphql_jwt.VerifyTokenMutation):
user = graphene.Field(User.__class__)
@classmethod
def resolve_mutant(cls, root, info, **kwargs):
return cls(user=info.context.user)
class Mutation(graphene.ObjectType):
token_auth = ObtainJSONWebToken.Field()
verify_token = VerifyToken.Field()
refresh_token = RefreshToken.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
# Example usage (run a mutation against the schema)
if __name__ == "__main__":
print("\n--- Attempting to obtain token ---")
obtain_token_query = """
mutation ObtainToken {
tokenAuth(username: "testuser", password: "password123") {
token
user {
username
email
}
}
}
"""
result = schema.execute(obtain_token_query)
if result.errors:
print("Errors:", result.errors)
else:
print("Data:", result.data)
token = result.data['tokenAuth']['token']
print("\n--- Attempting to verify token ---")
verify_token_query = f"""
mutation VerifyToken {{
verifyToken(token: "{token}") {{
payload
user {{
username
}}
}}
}}
"""
result_verify = schema.execute(verify_token_query)
if result_verify.errors:
print("Errors:", result_verify.errors)
else:
print("Data:", result_verify.data)