Skip to content

Slice 7: jotsmith token mint v1 (standard claims, KV Sign integration) #7

@MaxAnderson95

Description

@MaxAnderson95

What to build

Implement jotsmith token mint with standard claims only (--sub, --aud, --exp, --iat, --nbf, --jti, --verbose). This is the slice that finally proves the full minting story: client-side header + payload assembly, SHA-256 digest of the signing input, Azure Key Vault Sign("RS256", digest), base64url-encode the returned signature, emit the compact JWT to stdout. Custom-claim plumbing comes in #8.

The iss claim always comes from config — there is no --iss flag and there must never be one (PRD hard invariant; faking iss defeats the trust model).

Acceptance criteria

  • --sub required; absence → exit 2 with clear stderr error
  • --aud optional; single occurrence → JSON string; multiple → JSON array of strings; absent → omitted from payload
  • --exp accepts a Go time.Duration (15m, 1h, 24h) relative to iat, or an RFC3339 absolute timestamp; default 15m
  • --iat defaults to wall-clock now(); --nbf defaults to iat; --jti defaults to a fresh UUID v4
  • iss is read from config; no --iss flag exists, and attempting --iss fails as an unknown flag
  • Header is canonical JSON {"alg":"RS256","typ":"JWT","kid":"<thumbprint>"} with no whitespace
  • Payload is canonical JSON of the assembled claims with no whitespace
  • Signing flow: compute digest = SHA-256(base64url(header) + "." + base64url(payload)) client-side, then call KV Sign("RS256", digest); base64url-encode the returned signature; concatenate signing_input + "." + signature into the compact JWT
  • No private key material ever touches the CLI process — verified by code review
  • Stdout receives exactly the compact JWT followed by \n; nothing else, ever, on stdout from this command (smoke test asserts byte exactness)
  • --verbose pretty-prints decoded header, payload, and metadata to stderr after the JWT is on stdout
  • Exit 0 on success; non-zero on validation or KV signing failure
  • --exp > 24h is refused by default with a clear stderr error; --allow-long-lived overrides (resolves PRD §10 OQ Slice 3: jotsmith token decode #3)
  • PRD §10 OQ Slice 1: Repo scaffolding (Go module, urfave/cli v3 root, global flags, CI, goreleaser) #1 resolved: no temp-file output mode added — users redirect stdout themselves
  • Unit test pipes mint output to a parser and asserts the only stdout content is a valid 3-segment JWT terminated by exactly one newline
  • Integration test behind //go:build integration mints a token and asserts compact-form parsing

Blocked by

Originally created in OpenCode session ID: ses_17ca8efd8ffexLcFSysAMDVNBQ

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-agentFully specified, ready for an AFK agent to pick up

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions