HelloJohn / docs
SDKsPython SDK

Django

Integrate HelloJohn authentication into Django — middleware, decorators, DRF integration, and user sync patterns.

Django

Use HelloJohn with Django and Django REST Framework (DRF) for token-based authentication.


Installation

pip install hellojohn djangorestframework

Setup

In settings.py:

HELLOJOHN_TENANT_ID = os.environ["HELLOJOHN_TENANT_ID"]
HELLOJOHN_SECRET_KEY = os.environ["HELLOJOHN_SECRET_KEY"]

# Add to INSTALLED_APPS if using provided views
INSTALLED_APPS = [
    ...
    "rest_framework",
]

Initialize the client (e.g., in apps.py or a clients.py module):

# myapp/clients.py
from hellojohn import HelloJohn
import os

hj = HelloJohn(
    tenant_id=os.environ["HELLOJOHN_TENANT_ID"],
    secret_key=os.environ["HELLOJOHN_SECRET_KEY"],
)

Django REST Framework Authentication

Create a custom DRF authentication class:

# myapp/authentication.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from hellojohn import InvalidTokenError
from .clients import hj

class HelloJohnAuthentication(BaseAuthentication):
    def authenticate(self, request):
        auth_header = request.META.get("HTTP_AUTHORIZATION", "")
        if not auth_header.startswith("Bearer "):
            return None  # No token — allow other authenticators

        token = auth_header.removeprefix("Bearer ")

        try:
            payload = hj.verify_token(token)
        except InvalidTokenError as e:
            raise AuthenticationFailed(str(e))

        # Return (user, token) — user can be a simple object
        return (HJUser(payload), token)


class HJUser:
    """Lightweight user object populated from JWT claims."""
    def __init__(self, payload):
        self.id = payload.sub
        self.email = payload.email
        self.org_id = payload.org_id
        self.org_role = payload.org_role
        self.is_authenticated = True

Register it in settings.py:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "myapp.authentication.HelloJohnAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
}

Protected Views

from rest_framework.decorators import api_view
from rest_framework.response import Response

@api_view(["GET"])
def me(request):
    # request.user is an HJUser instance
    return Response({"userId": request.user.id})

@api_view(["POST"])
def create_post(request):
    post = Post.objects.create(
        author_id=request.user.id,
        title=request.data["title"],
        content=request.data["content"],
    )
    return Response(PostSerializer(post).data, status=201)

Custom Permission Class

from rest_framework.permissions import BasePermission

class IsOrgAdmin(BasePermission):
    def has_permission(self, request, view):
        return (
            request.user.is_authenticated
            and request.user.org_role in ("admin", "owner")
        )

# Usage
@api_view(["DELETE"])
@permission_classes([IsOrgAdmin])
def delete_member(request, user_id):
    # Only org admins/owners can access
    ...

Function-Based View Decorator

For non-DRF views:

from functools import wraps
from django.http import JsonResponse
from .clients import hj
from hellojohn import InvalidTokenError

def require_auth(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        token = request.META.get("HTTP_AUTHORIZATION", "").removeprefix("Bearer ")
        if not token:
            return JsonResponse({"error": "Unauthorized"}, status=401)
        try:
            request.auth = hj.verify_token(token)
        except InvalidTokenError:
            return JsonResponse({"error": "Invalid token"}, status=401)
        return view_func(request, *args, **kwargs)
    return wrapper

@require_auth
def protected_view(request):
    return JsonResponse({"userId": request.auth.sub})

Syncing Users to Your Database

Use HelloJohn webhooks to keep your Django User model in sync:

# myapp/webhooks.py
import json
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .clients import hj
from .models import Profile

@csrf_exempt
def hellojohn_webhook(request):
    # Verify webhook signature
    try:
        event = hj.webhooks.verify(
            payload=request.body,
            signature=request.META.get("HTTP_HJ_SIGNATURE", ""),
        )
    except Exception:
        return HttpResponse(status=400)

    if event.type == "user.created":
        Profile.objects.get_or_create(
            hellojohn_id=event.data["id"],
            defaults={"email": event.data["email"]},
        )
    elif event.type == "user.deleted":
        Profile.objects.filter(hellojohn_id=event.data["id"]).delete()

    return HttpResponse(status=200)

On this page