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.subWithout 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 payloadDecorator 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})