Jaunt
Guides

JWT Walkthrough

The hero 'wow gap' demo: ~50 lines of spec, 100+ lines of correct JWT code.

This is the Jaunt story in one page: a readable spec stub becomes a pile of boring correctness work — strict parsing, HMAC verification, expiry checks, and runnable tests.

Running this guide calls the OpenAI API and will spend tokens. Make sure OPENAI_API_KEY is set.

What We're Building

A minimal HS256 JWT implementation from a single spec file:

  • A Claims model (Pydantic)
  • create_token(user_id, secret, ttl=...) -> str
  • verify_token(token, secret) -> Claims
  • rotate_token(token, secret, ttl=...) -> str

The Spec

Full file: examples/jwt_auth/src/jwt_demo/specs.py

"""JWT Authentication — Jaunt Example"""
from __future__ import annotations

from datetime import timedelta

import jaunt
from pydantic import BaseModel


class Claims(BaseModel):
    """Decoded token payload."""

    sub: str   # subject (user id)
    iat: float # issued-at (unix timestamp)
    exp: float # expiry (unix timestamp)


@jaunt.magic()
def create_token(
    user_id: str, secret: str, *, ttl: timedelta = timedelta(hours=1)
) -> str:
    """
    Create an HS256-signed JWT.

    Structure: base64url(header) . base64url(payload) . base64url(signature)
    Header:  {"alg": "HS256", "typ": "JWT"}
    Payload: {"sub": user_id, "iat": <now>, "exp": <now + ttl>}

    - Use HMAC-SHA256 with `secret` as the key.
    - base64url encoding must omit padding ("=" characters).
    - Raise ValueError if user_id is empty.
    """
    raise RuntimeError("spec stub (generated at build time)")


@jaunt.magic(deps=[create_token, Claims])
def verify_token(token: str, secret: str) -> Claims:
    """
    Verify an HS256-signed JWT and return its claims.

    Steps:
    1. Split token on "." — must have exactly 3 parts.
    2. Recompute HMAC-SHA256 over header.payload; compare to signature.
    3. Decode payload JSON into Claims.
    4. Check exp > current time.

    Errors:
    - Raise ValueError("malformed") if structure is wrong.
    - Raise ValueError("invalid signature") if HMAC doesn't match.
    - Raise ValueError("expired") if token has expired.
    """
    raise RuntimeError("spec stub (generated at build time)")


@jaunt.magic(deps=[create_token, verify_token])
def rotate_token(
    token: str, secret: str, *, ttl: timedelta = timedelta(hours=1)
) -> str:
    """
    Verify an existing token and issue a fresh one for the same subject.

    - Verify the old token (propagate any errors).
    - Create a new token with the same user_id and a fresh ttl.
    - The rotated token MUST have strictly increasing iat/exp.
    """
    raise RuntimeError("spec stub (generated at build time)")

That's the entire spec. ~50 lines of intent, zero implementation logic.

Run The Build

From the repo root:

uv sync
export OPENAI_API_KEY=...
uv run jaunt build --root examples/jwt_auth

Jaunt writes the generated implementation to:

examples/jwt_auth/src/jwt_demo/__generated__/specs.py

Look At The Output

You wrote ~50 lines of spec. Jaunt generated 100+ lines of correct, edge-case-handling code. Here are excerpts:

Base64url helper (no padding):

def _b64url_encode_no_pad(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")

JWT assembly with canonical JSON:

header_json = json.dumps(header_obj, separators=(",", ":"), sort_keys=True).encode("utf-8")
payload_json = json.dumps(payload_obj, separators=(",", ":"), sort_keys=True).encode("utf-8")

header_b64 = _b64url_encode_no_pad(header_json)
payload_b64 = _b64url_encode_no_pad(payload_json)
signing_input = f"{header_b64}.{payload_b64}"

sig = hmac.new(secret.encode("utf-8"), signing_input.encode("utf-8"), hashlib.sha256).digest()
sig_b64 = _b64url_encode_no_pad(sig)
return f"{signing_input}.{sig_b64}"

Signature verification and expiry checks:

# Recompute HMAC and compare
expected_sig = hmac.new(
    secret.encode("utf-8"), signing_input.encode("utf-8"), hashlib.sha256
).digest()
if not hmac.compare_digest(actual_sig, expected_sig):
    raise ValueError("invalid signature")

# Check expiry
if payload["exp"] <= time.time():
    raise ValueError("expired")

All the boring-but-critical stuff: padding removal, constant-time comparison, JSON canonicalization, expiry enforcement. You specified what; Jaunt did how.

Generate And Run Tests

PYTHONPATH=examples/jwt_auth/src uv run jaunt test --root examples/jwt_auth

Jaunt writes generated tests to examples/jwt_auth/tests/__generated__/specs.py.

Example generated test excerpt:

def test_roundtrip_create_and_verify() -> None:
    api = _load_api()
    token = api.create_token("user-42", "s3cret")
    claims = api.verify_token(token, "s3cret")
    assert getattr(claims, "sub") == "user-42"
    assert getattr(claims, "exp") > getattr(claims, "iat")

The Skills Bonus

Because the spec imports pydantic, Jaunt auto-generates a PyPI skill at build time:

examples/jwt_auth/.agents/skills/pydantic/SKILL.md

This gives the LLM concrete Pydantic usage patterns, improving the quality of generated code that uses external libraries.

See: Auto-Generated PyPI Skills.

Next: Adding Jaunt To Your Project.

On this page