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 →
in reviewhumanPR #156Auth

hackathon/mwarsss-delegatable-auth

Implements Problem 04 → Auth Capability Delegation from the NandaHack charter. Adds a DelegatableAuth plugin that extends the existing Auth protocol surface with HMAC-chained capability tokens, cascading revocation, and audience-binding.

Author

Status
In review
Opened on
Jul 10
Branch
hackathon/mwarsss-delegatable-auth

Description

The pitch.

## Summary

Implements **Problem 04 → Auth Capability Delegation** from the NandaHack charter.  Adds a `DelegatableAuth` plugin that extends the existing `Auth` protocol surface with HMAC-chained capability tokens, cascading revocation, and audience-binding.

- **Plugin** → `DelegatableAuth` (auth layer, name `delegatable`).  Extends the `Auth` protocol (`issue` / `verify` / `revoke`) with an additive `delegate(parent_token, audience, scopes, ttl, *, caller) → Token` method.  No existing callers are modified.
- **Four attacks blocked** → scope escalation, stale-parent revocation, audience confusion at `verify()`, and audience confusion at `delegate()`.  Each attack raises a distinct typed exception (`ScopeEscalationError`, `RevokedAncestorError`, `AudienceConfusionError`), all `ValueError` subclasses for drop-in compatibility.
- **HMAC chain** → based on Birgisson et al. 2014 (Macaroon construction).  Each child token's signing key is derived from the parent's HMAC output, so revoking a parent transitively invalidates every descendant without a per-child revocation list.
- **Audience binding** → both `verify(token, *, caller)` and `delegate(token, audience, scopes, ttl, *, caller)` enforce the audience check unconditionally on audience-bound tokens.  Omitting `caller=` on a delegated token is rejected at both call sites — the same as passing a wrong caller.  A gap where `verify()` silently accepted audience-bound tokens when `caller=` was omitted was found and closed in the same review cycle as the `delegate()` fix.
- **Three adversarial validators** → `check_scope_escalation_blocked`, `check_stale_parent_blocked`, `check_audience_confusion_blocked` in `nest_plugins_reference/validators/auth_validators.py`, returning `ValidatorReport` (same type as `gossip_validators.py`).  Each validator is proven against a JWT baseline that deliberately fails.
- **Scenario** → 16-agent two-level tree: 1 coordinator → 3 intermediaries → 12 leaves.  Scenario YAML at `scenarios/delegated_auth.yaml` with `layers.auth: delegatable`.
- **Wiring** → `_BUILTINS` in `plugins.py`, `_try_load_builtin` in `scenarios.py`, and a `[project.entry-points."nest.plugins.auth"]` entry in `pyproject.toml`.

## What differentiates this from other Problem 04 submissions

Most open PRs on Problem 04 implement a single signature scheme or a flat token list. This submission is distinct on three concrete axes:

1. **Key-chain revocation without a per-child revocation list.** Revoking the root invalidates every descendant transitively because each child's signing key is the HMAC of its parent's signature — there is no registry of child IDs to maintain. The mechanism is O(depth) to verify, O(1) to revoke.

2. **Typed, ValueError-compatible exceptions.** `ScopeEscalationError`, `RevokedAncestorError`, and `AudienceConfusionError` are all `ValueError` subclasses. Callers that catch `ValueError` get correct behaviour for free; callers that want to distinguish attack types can catch the specific subclass.

3. **Validators proven against a JWT baseline.** Each of the three adversarial validators (`check_scope_escalation_blocked`, `check_stale_parent_blocked`, `check_audience_confusion_blocked`) is run against both `DelegatableAuth` (must pass) and the reference `JwtAuth` (must fail). This dual-proof structure means the validator itself is tested, not just asserted.

## Fixes since the original submission (PR #103)

Maintainer review raised four issues; all are resolved in this PR.  A fifth gap was found and closed during the same review cycle:

| # | Issue | Fix |
|---|-------|-----|
| 1 | Startup crash — factory passed plugin class, not instance | `delegated_auth_factory` now instantiates `DelegatableAuth(clock=0.0)` using the same `isinstance(cls, type)` guard as `marketplace.py` and `empic_payments.py` |
| 2 | Non-deterministic token IDs (`uuid4()`) | Replaced with per-instance monotonic counter (`tok-0001`, `tok-0002`, …) |
| 3 | Non-deterministic timestamps (`time.time(

…

Try it

Open PR on GitHubView diff

Checkout locally

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