Skip to main content

Ariadne JWT

JSON Web Token for Ariadne Django

Installation

pip install ariadne-jwt

Include the JSONWebTokenMiddleware in your MIDDLEWARE settings:

MIDDLEWARE = [
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'ariadne_jwt.middleware.JSONWebTokenMiddleware',
]

Include the JSONWebTokenBackend in your AUTHENTICATION_BACKENDS settings:

AUTHENTICATION_BACKENDS = [
    'ariadne_jwt.backends.JSONWebTokenBackend',
    'django.contrib.auth.backends.ModelBackend'
]

Schema

Add mutations to your GraphQL schema

import ariadne
from ariadne_jwt import resolve_verify, resolve_refresh, resolve_token_auth, jwt_schema, GenericScalar

type_defs = '''
    type Mutation {
        ...
        verifyToken(token: String!): VerifyToken
        refreshToken(token: String!): RefreshToken
        tokenAuth(username: String!, password:String!): TokenAuth
        ...
    }
    '''

mutation = ariadne.MutationType()

mutation.set_field('verifyToken', resolve_verify)
mutation.set_field('refreshToken', resolve_refresh)
mutation.set_field('tokenAuth', resolve_token_auth)

schema = ariadne.make_executable_schema([type_defs, jwt_schema], mutation, GenericScalar)
  • tokenAuth to authenticate the user and obtain the JSON Web Token.

The resolver uses 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.
mutation RefreshToken($token: String!) {
    refreshToken(token: $token) {
        token
        payload
    }
}

Authentication in GraphQL queries

Now in order to access protected API you must include the Authorization: JWT <token> header. you can use the login_required() decorator for your resolvers:

from ariadne import QueryType
from ariadne_jwt.decorators import login_required

type_defs = '''
type UserNode {
    username:String
    email: String
}
type Query {
    me: UserNode
}
'''

query = QueryType()


@query.field('me')
@login_required
def resolve_viewer(self, info, **kwargs):
    return info.context.get('request').user

Customizing

If you want to customize the tokenAuth behavior, you'll need to extend the TokenAuth type and write a resolver with @token_auth decorator.

from ariadne_jwt.decorators import token_auth

extended_type_defs = '''
type UserNode {
    id
    username
    email
}
extend type TokenAuth {
    user: UserNode
}
'''


@token_auth
def resolve_token_auth(obj, info, **kwargs):
    return {'user': info.context.get('request').user}
mutation TokenAuth($username: String!, $password: String!) {
    tokenAuth(username: $username, password: $password) {
        token
        user {
            id
        }
    }
}

Settings

ariadne-jwt reads your configuration from a single Django setting named GRAPHQL_JWT

GRAPHQL_JWT = {
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_EXPIRATION_DELTA': timedelta(seconds=60 * 10)
}

Default Settings

DEFAULTS = {
    # Algorithm for cryptographic signing
    'JWT_ALGORITHM': 'HS256',

    # Identifies the recipients that the JWT is intended for
    'JWT_AUDIENCE': None,

    # Identifies the principal that issued the JWT
    'JWT_ISSUER': None,

    # Validate an expiration time which is in the past but not very far
    'JWT_LEEWAY': 0,

    # The secret key used to sign the JWT
    'JWT_SECRET_KEY': settings.SECRET_KEY,

    # Secret key verification
    'JWT_VERIFY': True,

    # Expiration time verification
    'JWT_VERIFY_EXPIRATION': False,

    # Timedelta added to utcnow() to set the expiration time
    'JWT_EXPIRATION_DELTA': timedelta(seconds=60 * 5),

    # Enable token refresh
    'JWT_ALLOW_REFRESH': True,

    # Limit on token refresh
    'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7),

    # Enable long time running refresh token
    'JWT_LONG_RUNNING_REFRESH_TOKEN': False,

    # The model to use to represent a refresh token
    'JWT_REFRESH_TOKEN_MODEL': 'refresh_token.RefreshToken',

    # Refresh token number of bytes
    'JWT_REFRESH_TOKEN_N_BYTES': 20,

    # Authorization header name
    'JWT_AUTH_HEADER': 'HTTP_AUTHORIZATION',

    # Authorization prefix
    'JWT_AUTH_HEADER_PREFIX': 'JWT',

    # A custom function *f(payload, context)* to encode the token
    'JWT_ENCODE_HANDLER': 'ariadne_jwt.utils.jwt_encode',

    # A custom function *f(token, context)* to decode the token
    'JWT_DECODE_HANDLER': 'ariadne_jwt.utils.jwt_decode',

    # A custom function *f(user, context)* to generate the token payload
    'JWT_PAYLOAD_HANDLER': 'ariadne_jwt.utils.jwt_payload',

    # A custom function `f(payload)` to obtain the username    
    'JWT_PAYLOAD_GET_USERNAME_HANDLER': (lambda payload: payload.get(get_user_model().USERNAME_FIELD)),

    # A custom function `f(orig_iat, context)` to determine if refresh has expired
    'JWT_REFRESH_EXPIRED_HANDLER': 'ariadne_jwt.utils.refresh_has_expired',
}

Writing tests

from django.contrib.auth import get_user_model
from ariadne_jwt.testcases import JSONWebTokenTestCase


class UserTests(JSONWebTokenTestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(username='test', password='dolphins')
        self.client.authenticate(self.user)
        self.client.schema(type_defs, resolvers, directives=directives)

    def test_get_user(self):
        query = '''
            query GetUser($username: String) {
                user(username: $username) {
                    id
                }
            }
            '''
        self.client.execute(query, variables={'username': self.user.username})

Testing the library

run the following in root directory

python run_tests.py

Metadata

Release files for ariadne-jwt 0.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ariadne-jwt 0.1.7
File Size Uploaded
ariadne-jwt-0.1.7.tar.gz 14.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ariadne-jwt 0.1.7
File Interpreter ABI Platform
ariadne_jwt-0.1.7-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 33.6 kB

Release files / ariadne-jwt-0.1.7.tar.gz

Download URL ariadne-jwt-0.1.7.tar.gz
Size 14.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4e5082af612fc4995f0771b3e26e380210979c8401b6a882903c96d4d41a8492
BLAKE2b-256 checksum
How to use checksums
21bde4a1c88880e441bce18068eb920b7552185150f6b7c7ebbd57ba58ad4214
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.1.post20200807 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.7.7

Release files / ariadne_jwt-0.1.7-py2.py3-none-any.whl

Download URL ariadne_jwt-0.1.7-py2.py3-none-any.whl
Size 19.0 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
4aa42050bf5b9d3b6394e867cfbc288d831596d2fe6db17e39aaff4a7fb9cf10
BLAKE2b-256 checksum
How to use checksums
c8cd44273777235d7afe51c8e868eb9e770c43c2755bcd64053869ee697b35d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/49.2.1.post20200807 requests-toolbelt/0.9.1 tqdm/4.48.2 CPython/3.7.7

Release history Release notifications | RSS feed

This release

0.1.7 This release

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page