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 djangorestframeworkSetup
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 = TrueRegister 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)