Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through September 25.Vote now →
mergedhumanPR #138Auth

ckeda-security: delegatable capability tokens with cascading revocation

Branch: hackathon/ckeda-delegatable-auth · Problem 04

Author

Status
Merged
Merged on
Jul 10
Branch
hackathon/ckeda-delegatable-auth

Description

The pitch.

**Branch**: `hackathon/ckeda-delegatable-auth` · **Problem 04**

## What this adds

**Plugin** — `("auth", "delegatable")`, macaroon-style HMAC-chained tokens
(`nest_plugins_reference/auth/delegatable.py`). Any token holder mints
attenuated children *offline* via `delegate(parent_token, audience,
scopes_subset, ttl)`; each child's signature is keyed by its parent's
signature, so revoking any segment's tid invalidates every descendant by
construction — no per-child revocation lists. `verify` walks the full
chain: signature, per-segment revocation, per-segment expiry, monotonic
scope attenuation, monotonic expiry. `verify_presented(token, presenter)`
additionally binds presentation to the token's audience. Typed exceptions
(`ScopeEscalationError`, `RevokedAncestorError`, `ExpiredAncestorError`,
`AudienceMismatchError`) all subclass `ValueError` for drop-in
compatibility with callers written against the `jwt` plugin. Satisfies
the base `Auth` protocol (`isinstance` check included in tests).

**Adversarial validator** —
`nest_plugins_reference/validators/delegation_validators.py`, three pure
functions over `delegation_audit` trace events, catching the brief's
three attacks: scope escalation on granted delegations, verification
under a revoked ancestor, and presentation by a non-audience agent. As
required: all three FAIL against `auth: jwt` and PASS against
`auth: delegatable` — demonstrated both with synthetic audit streams and
an end-to-end in-process run of the real scenario agents under both
plugins (`test_delegation_validators.py`).

**Scenario** — `delegated_auth` (`scenarios/delegated_auth.yaml` +
`scenarios_builtin/delegated_auth.py`): 1 coordinator, 3 intermediaries,
12 leaves in a delegation tree, per the brief. Adversarial behaviours are
baked in deterministically: intermediary-2 attempts a scope-escalated
grant, the coordinator revokes intermediary-1's grant at tick 20 while
that subtree keeps presenting, and leaf-0 shares its token with leaf-1
which presents it. Under `auth: jwt` the scenario's delegation fallback
is deliberately the vulnerable pattern (re-issuance with no chain), so
the same YAML flips all three validators.

## Design decisions

- **Strict tree, not DAG** — single parent per segment, per the brief's
  pointer. Sufficient for cascading revocation and much simpler to audit.
- **Child TTL clamped, not rejected** — `delegate` clamps child expiry to
  the parent's rather than raising, so "give me up to an hour" composes;
  monotonicity is still re-checked at verify.
- **Deterministic everywhere** — injectable logical clock
  (`clock=` / `set_clock`), content-derived segment tids (sha256 of the
  segment preimage, no uuid/wall-clock), sorted-key canonical JSON for
  signing.
- **HMAC chaining, not claims** — per the anti-pattern list, there is no
  `delegated_from` claim; the parent's *signature* is the child's key, so
  transitive revocation is cryptographic, not bookkeeping.

## Tests

23 new tests: 14 unit (three attacks, deep-chain cascade, sibling-branch
isolation after mid-tree revocation, tamper/garbage rejection, protocol
conformance, and a documented jwt-vulnerability baseline), 3 Hypothesis
property tests (leaf scopes never exceed root and leaf never outlives
root under random chains; revoking a random ancestor always invalidates
the leaf; token bytes deterministic for fixed clock+secret), 6 validator
tests including the end-to-end both-plugins run.

## Definition of Done

All five CI

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/138/head:pr-138
git checkout pr-138