HelloJohn / docs
SDKsPython SDK

Verify Token

Verify HelloJohn JWTs in Python — local JWKS verification, PyJWT integration, and token claim extraction.

Verify Token

Verify HelloJohn access tokens in your Python backend.


Using the SDK

from hellojohn import HelloJohn

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

verify_token

try:
    payload = hj.verify_token(token)
    user_id = payload.sub
except hellojohn.InvalidTokenError as e:
    # Token is invalid, expired, or malformed
    raise HTTPException(status_code=401, detail=str(e))

TokenPayload fields:

@dataclass
class TokenPayload:
    sub: str           # User ID
    sid: str           # Session ID
    iss: str           # Issuer
    aud: str           # Audience (tenant ID)
    iat: int           # Issued at (Unix timestamp)
    exp: int           # Expiry (Unix timestamp)
    org_id: str | None # Active organization ID
    org_role: str | None # Role in active org
    email: str | None  # User email (if in claims)

API-Based Verification (Remote)

For instant revocation detection:

result = await hj.verify_token_remote(token)
if not result.valid:
    raise HTTPException(status_code=401, detail="Invalid session")

user_id = result.payload.sub

Without the SDK (PyJWT + cryptography)

import jwt
import requests
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

TENANT_ID = os.environ["HELLOJOHN_TENANT_ID"]
JWKS_URL = f"https://api.hellojohn.dev/v1/jwks/{TENANT_ID}"

# Fetch and cache JWKS
jwks = requests.get(JWKS_URL).json()

def get_public_key(kid: str) -> Ed25519PublicKey:
    for key in jwks["keys"]:
        if key["kid"] == kid:
            # Convert JWK to public key...
            return load_jwk(key)
    raise ValueError(f"Key {kid} not found")

def verify_token(token: str) -> dict:
    header = jwt.get_unverified_header(token)
    public_key = get_public_key(header["kid"])

    payload = jwt.decode(
        token,
        public_key,
        algorithms=["EdDSA"],
        audience=TENANT_ID,
        issuer="https://api.hellojohn.dev",
    )
    return payload

Decorator Pattern

from functools import wraps
from flask import request, jsonify

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get("Authorization", "").removeprefix("Bearer ")
        if not token:
            return jsonify({"error": "Missing token"}), 401
        try:
            payload = hj.verify_token(token)
            request.auth = payload
        except hellojohn.InvalidTokenError:
            return jsonify({"error": "Invalid token"}), 401
        return f(*args, **kwargs)
    return decorated

@app.route("/api/me")
@require_auth
def get_me():
    return jsonify({"userId": request.auth.sub})

On this page