HelloJohn / docs
Sdks

Python SDK

Use the hellojohn Python package to verify tokens and manage users from FastAPI, Django, Flask, or any Python backend.

Python SDK

The hellojohn Python package provides token verification and full admin API access for Python backends.

Installation

pip install hellojohn

Setup

import hellojohn

client = hellojohn.Client(
    secret_key=os.environ["HJ_SECRET_KEY"],
    # Optional: for self-hosted
    api_url=os.environ.get("HJ_API_URL"),
)

Token verification

from hellojohn import verify_token, InvalidTokenError

def get_current_user(authorization: str):
    if not authorization.startswith("Bearer "):
        raise ValueError("Invalid authorization header")

    token = authorization.removeprefix("Bearer ")

    try:
        payload = verify_token(token, secret_key=os.environ["HJ_SECRET_KEY"])
        return payload
    except InvalidTokenError as e:
        raise Exception(f"Unauthorized: {e}")

# payload fields:
# payload.sub           — user ID
# payload.email         — user email
# payload.tenant_id     — tenant ID
# payload.role          — "admin" | "member"
# payload.custom_claims — dict of custom JWT claims

FastAPI integration

from fastapi import FastAPI, Depends, HTTPException, Header
from hellojohn import verify_token, InvalidTokenError, TokenPayload

app = FastAPI()

async def get_current_user(authorization: str = Header(...)) -> TokenPayload:
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Invalid authorization header")

    token = authorization.removeprefix("Bearer ")

    try:
        return verify_token(token, secret_key=os.environ["HJ_SECRET_KEY"])
    except InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid or expired token")

def require_role(role: str):
    async def dependency(user: TokenPayload = Depends(get_current_user)):
        if user.role != role:
            raise HTTPException(status_code=403, detail="Insufficient permissions")
        return user
    return dependency

# Protected route
@app.get("/api/me")
async def get_me(user: TokenPayload = Depends(get_current_user)):
    return {
        "user_id": user.sub,
        "email": user.email,
        "tenant_id": user.tenant_id,
        "role": user.role,
    }

# Admin-only route
@app.delete("/api/users/{user_id}")
async def delete_user(
    user_id: str,
    admin: TokenPayload = Depends(require_role("admin")),
    hj: hellojohn.Client = Depends(lambda: hellojohn.Client(os.environ["HJ_SECRET_KEY"])),
):
    await hj.users.delete(user_id)
    return {"deleted": True}

Django integration

# middleware.py
from hellojohn import verify_token, InvalidTokenError

class HelloJohnAuthMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        auth_header = request.META.get("HTTP_AUTHORIZATION", "")
        if auth_header.startswith("Bearer "):
            token = auth_header.removeprefix("Bearer ")
            try:
                request.hj_user = verify_token(
                    token,
                    secret_key=settings.HJ_SECRET_KEY,
                )
            except InvalidTokenError:
                request.hj_user = None
        else:
            request.hj_user = None

        return self.get_response(request)


# decorators.py
from functools import wraps
from django.http import JsonResponse

def require_auth(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.hj_user is None:
            return JsonResponse({"error": "Unauthorized"}, status=401)
        return view_func(request, *args, **kwargs)
    return wrapper

def require_role(role):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if request.hj_user is None or request.hj_user.role != role:
                return JsonResponse({"error": "Forbidden"}, status=403)
            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator


# views.py
@require_auth
def get_me(request):
    return JsonResponse({
        "user_id": request.hj_user.sub,
        "email": request.hj_user.email,
    })

User management

import asyncio
import hellojohn

client = hellojohn.Client(secret_key=os.environ["HJ_SECRET_KEY"])

# Async API
async def main():
    # List users
    result = await client.users.list(tenant_id="tnt_01H...", limit=50)
    for user in result.data:
        print(user.email)

    # Get a user
    user = await client.users.get("usr_01H...")

    # Create a user
    new_user = await client.users.create(
        email="alice@example.com",
        password="strongpassword",
        display_name="Alice",
        tenant_id="tnt_01H...",
        role="member",
    )

    # Update a user
    updated = await client.users.update("usr_01H...", display_name="Alice Smith")

    # Disable / delete
    await client.users.disable("usr_01H...")
    await client.users.delete("usr_01H...")

asyncio.run(main())

Synchronous client

# For sync contexts (Django views, scripts)
client = hellojohn.SyncClient(secret_key=os.environ["HJ_SECRET_KEY"])

users = client.users.list(tenant_id="tnt_01H...")
user = client.users.get("usr_01H...")

Webhook verification

# FastAPI webhook handler
from hellojohn import verify_webhook_signature, WebhookVerificationError

@app.post("/webhooks/hellojohn")
async def handle_webhook(
    request: Request,
    x_hellojohn_signature: str = Header(...),
    x_hellojohn_timestamp: str = Header(...),
):
    body = await request.body()

    try:
        verify_webhook_signature(
            payload=body,
            signature=x_hellojohn_signature,
            timestamp=x_hellojohn_timestamp,
            secret=os.environ["WEBHOOK_SECRET"],
        )
    except WebhookVerificationError:
        raise HTTPException(status_code=400, detail="Invalid webhook signature")

    event = await request.json()

    if event["type"] == "user.created":
        await on_user_created(event["data"])
    elif event["type"] == "user.login":
        await on_user_login(event["data"])

    return {"received": True}

Error handling

from hellojohn.exceptions import (
    HelloJohnError,
    InvalidTokenError,
    UserNotFoundError,
    TenantNotFoundError,
    RateLimitError,
    ValidationError,
)

try:
    user = await client.users.get("usr_nonexistent")
except UserNotFoundError:
    print("User not found")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except HelloJohnError as e:
    print(f"Error {e.code}: {e.message}")

Async vs sync

Use caseClient
FastAPI, Starlette, aiohttphellojohn.Client (async)
Django (sync views)hellojohn.SyncClient
Flaskhellojohn.SyncClient
Background scriptsEither

The async Client uses httpx.AsyncClient internally. The sync SyncClient uses httpx.Client.

On this page