in reviewhumanPR #70Auth
fayner-delegatable-auth: macaroon capability tokens with cascading revocation (problem 04)
Problem: #04 — Delegatable capability tokens with cascading revocation (auth layer).
Author
@faynercosta
github profile →- Status
- In review
- Opened on
- Jul 5
- Branch
- hackathon/fayner-delegatable-auth
Description
The pitch.
# [Hackathon] fayner-delegatable-auth: macaroon capability tokens with cascading revocation
**Problem:** #04 — Delegatable capability tokens with cascading revocation (auth layer).
**Persona:** authorization-infrastructure engineer. The risk model, the test
emphasis (adversarial subset/revocation/audience invariants), and the
macaroon idioms below are the submission, not a label on a generic plugin.
> **Naming note (post-#138):** while this PR was in review, #138 merged an
> independent problem-04 solution under `auth/delegatable.py`,
> `DelegatableAuth`, `("auth", "delegatable")` and the `delegated_auth`
> scenario. This submission keeps its implementation unchanged and takes a
> distinct, honest identity so both plugins coexist: **`MacaroonAuth`**,
> registered as **`("auth", "macaroon")`** (the mechanism *is* a macaroon;
> the name says so), with the **`capability_delegation`** scenario — matching
> the problem brief's own slug, `04-auth-capability-delegation`.
## What sets this apart from the other problem-04 submissions
Most delegatable-token PRs (including strong ones) stop at "the HMAC chain
verifies." This one is built around the observation that **the chain proves
provenance, not permission** — any holder can extend the chain with whatever
link they like and compute a perfectly valid signature. Concretely:
1. **Verify-side semantic re-walk.** `verify` independently re-checks scope
subsets, TTL nesting, and delegator→audience linkage across *every* link,
so a forged-but-chain-valid broadening link is rejected
(`test_scope_escalation_rejected_at_verify_when_chain_tampered` exercises
exactly this, with a hand-forged HMAC-valid token).
2. **Both directions of the adversarial bar are demonstrated, not asserted.**
Every attack probe runs against *both* plugins through small adapters
(`macaroon_delegate`/`presenter_verify` vs `naive_jwt_delegate`/
`blind_verify`): the probes pass against `macaroon` **and** fail against
the reference `jwt`, in the same test run.
3. **Offline trace-replay validator.** The scenario's gatekeeper emits a
one-line audit protocol; `validate_capability_delegation_trace` replays it
offline and independently decides pass/fail — it FAILS on the `jwt` arm
and PASSES on the `macaroon` arm.
4. **Four Hypothesis properties** (subset invariant at every depth, monotone
revocation cascade, deterministic token construction, escalation always
rejected) on top of the unit and end-to-end suites.
5. **Determinism in both arms, asserted.** Byte-identical traces are asserted
for the `macaroon` *and* the `jwt` arm — the reference plugin is pinned to
the logical clock at provisioning (`clock=0.0`), so no wall-clock leaks
into any trace this scenario writes.
## Motivation
The default `jwt` plugin can mint and revoke flat tokens, but it cannot model
the single most common multi-agent authorization pattern: an orchestrator
holding a long-lived root capability, minting narrowly-scoped, short-lived
sub-capabilities for worker agents **without going back to the issuer**, and
having a single `revoke(parent)` cascade to every descendant. `JwtAuth._revoked`
is a set of exact token strings with no parent-child relationship, so revoking a
parent leaves its delegated children valid — the exact gap the problem calls
out, and the gap that makes "an LLM agent sub-renting a tool and cleanly
withdrawing it" impossible to express today.
## Design
`MacaroonAuth` is a macaroon (Birgisson et al., NDSS 2014): a token is a
chain of links, each an attenuating caveat, secured by an HMAC chain
`sig_i = HMAC(sig_{i-1}, link_i)` anchored at the verifier's root secret. Three
properties follow:
- **Offline attenuation.** Any holder can mint a narrower child (`attenuate`,
or the verifier-checked `delegate`) because extending the chain only needs
the parent's signature, not the root secret. This is real delegation, not
central re-issuance (the named anti-pattern).
- **Cascading revocati
…Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/70/head:pr-70
git checkout pr-70