JSON Web Token authentication for Django GraphQL
- Django ≥ 1.11
Install last stable version from Pypi.
pip install django-graphql-jwt
Include the JSONWebTokenMiddleware
middleware in your MIDDLEWARE settings:
MIDDLEWARE = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'graphql_jwt.middleware.JSONWebTokenMiddleware',
...
]
Include the JSONWebTokenBackend
backend in your AUTHENTICATION_BACKENDS settings:
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
]
Add mutations to the root schema.
import graphene
import graphql_jwt
class Mutations(graphene.ObjectType):
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.Verify.Field()
refresh_token = graphql_jwt.Refresh.Field()
schema = graphene.Schema(mutation=Mutations)
tokenAuth
to authenticate the user and obtain the JSON Web Token.
The mutation uses your User's model USERNAME_FIELD, which by default is username
.
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
}
}
verifyToken
to confirm that the token is valid.
mutation VerifyToken($token: String!) {
verifyToken(token: $token) {
payload
}
}
refreshToken
to obtain a brand new token with renewed expiration time for non-expired tokens.
Configure your refresh token scenario and set the flag JWT_VERIFY_EXPIRATION=true
.
mutation RefreshToken($token: String!) {
refreshToken(token: $token) {
token
payload
}
}
Now in order to access protected API you must include the Authorization: JWT <token>
header.
Django-graphql-jwt uses middleware to hook the authenticated user into request object. The simple, raw way to limit access to data is to check info.context.user.is_authenticated
:
import graphene
class Query(graphene.ObjectType):
viewer = graphene.Field(UserType)
def resolve_viewer(self, info, **kwargs):
user = info.context.user
if not user.is_authenticated:
raise Exception('Authentication credentials were not provided')
return user
As a shortcut, you can use the login_required()
decorator for your resolvers and mutations:
See the documentation for the full list of decorators.
import graphene
from graphql_jwt.decorators import login_required
class Query(graphene.ObjectType):
viewer = graphene.Field(UserType)
@login_required
def resolve_viewer(self, info, **kwargs):
return info.context.user
Complete support for Relay.
import graphene
import graphql_jwt
class Mutations(graphene.ObjectType):
token_auth = graphql_jwt.relay.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.relay.Verify.Field()
refresh_token = graphql_jwt.relay.Refresh.Field()
Relay mutations only accepts one argument named input, read the documentation for more info.
If you want to customize the ObtainJSONWebToken
behavior, you'll need to customize the resolve()
method on a subclass of JSONWebTokenMutation
or .relay.JSONWebTokenMutation
.
import graphene
import graphql_jwt
class ObtainJSONWebToken(graphql_jwt.JSONWebTokenMutation):
user = graphene.Field(UserType)
@classmethod
def resolve(cls, root, info):
return cls(user=info.context.user)
Authenticate the user and obtain the token and the user id.
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
user {
id
}
}
}
This package includes a subclass of unittest.TestCase and improve support for making GraphQL queries using JSON Web Token authentication.
from django.contrib.auth import get_user_model
from graphql_jwt.testcases import JSONWebTokenTestCase
class UsersTests(JSONWebTokenTestCase):
def setUp(self):
self.user = get_user_model().objects.create(username='test')
self.client.authenticate(self.user)
def test_users(self):
query = '''
query GetUsers($username: String) {
users(username: $username) {
id
}
}'''
self.client.execute(query, variables={'username': self.user.username})
Django-graphql-jwt reads your configuration from a single Django setting named GRAPHQL_JWT
GRAPHQL_JWT = {
'JWT_EXPIRATION_DELTA': timedelta(minutes=10),
}
Here's a list of settings available in Django-graphql-jwt and their default values.
Algorithm for cryptographic signing
Default: 'HS256'
Identifies the recipients that the JWT is intended for
Default: None
Identifies the principal that issued the JWT
Default: None
Validate an expiration time which is in the past but not very far
Default: timedelta(seconds=0)
The secret key used to sign the JWT
Default: settings.SECRET_KEY
Secret key verification
Default: True
Expiration time verification
Default: False
JWT_EXPIRATION_DELTA
Timedelta added to utcnow() to set the expiration time
Default: timedelta(minutes=5)
JWT_ALLOW_REFRESH
Enable token refresh
Default: True
JWT_REFRESH_EXPIRATION_DELTA
Limit on token refresh
Default: timedelta(days=7)
JWT_AUTH_HEADER
Authorization header name
Default: 'HTTP_AUTHORIZATION'
JWT_AUTH_HEADER_PREFIX
Authorization prefix
Default: 'JWT'
JWT_PAYLOAD_HANDLER
A custom function `f(user, context)` to generate the token payload
Default: 'graphql_jwt.utils.jwt_payload'
JWT_ENCODE_HANDLER
A custom function `f(payload, context)` to encode the token
Default: 'graphql_jwt.utils.jwt_encode'
JWT_DECODE_HANDLER
A custom function `f(token, context)` to decode the token
Default: 'graphql_jwt.utils.jwt_decode'
JWT_PAYLOAD_GET_USERNAME_HANDLER
A custom function `f(payload)` to obtain the username
Default: lambda payload: payload.get(get_user_model().USERNAME_FIELD)
Credits and thanks.