跳到正文
DEV Community· DarkEdges·· 3 小時前AI 評分56

作者在 AAuth 上重建 Biscuit 信任實驗室,對照兩種授權協議

Same lab, different protocol: rebuilding the Biscuit trust lab on AAuth

AI 導讀

DarkEdges 將 Relay Biscuit 信任實驗室重建為 AAuth 版本,以相同操作方和七個情境比較兩種協議的授權方式與成本。Biscuit 由接收方本地決策並支援離線收窄權限;AAuth 每次請求均簽署,由 Alice 的 person server(PS)按已批准的 mission 核發 token,接收方驗證後套用本地政策。

正文

The first four parts of this series built a delegation network on Biscuit: Alice asks PlannerCo for research, PlannerCo hands work to ResearchCo and ComputeCo, ResearchCo hands search to SearchCo, and a clearinghouse bills Alice's organisation for all of it.

This part rebuilds the same lab on AAuth, an HTTP authorization protocol in which agents sign every request and a person server issues the tokens. It has the same operators, the same seven scenarios, the same trust editor and the same clearinghouse. Every scenario settles the same credits as the Biscuit version: 40 / 30 / 30 / 30 / 65 / 40 / 30.

What changes is where each decision is made, and what that costs.

GitHub logo darkedges / aauth-lab

AAuth (draft-hardt-oauth-aauth-protocol-11) version of the Relay Biscuit lab, with a side-by-side comparison

Relay — AAuth network lab

check

The Relay Biscuit lab rebuilt on AAuth. It has the same operators, scenarios, budget rules and UI. Biscuit attenuation is replaced by AAuth's signed HTTP requests, person-server tokens and call chaining. Signatures, tokens and HTTP exchanges are real. Operators, work and credits are simulated.

Live demo: aauth.demos.darkedges.com

Relay AAuth playground showing the agent network, credit ledger, the approved mission with per-hop auth tokens, and the identity and receiver checks for SearchCo

See COMPARISON.md for a side-by-side analysis of the two solutions.

Run locally

Use Node.js 22 or newer and pnpm 11:

pnpm install
pnpm dev

Open http://127.0.0.1:8796. The Biscuit lab uses 8795, so both labs can run side by side. On Windows, run ./start.ps1. The interface starts a demo run automatically.

pnpm check   # behaviour, spec-conformance, reference-client interop and debug formatting tests

How the AAuth version works

Each run creates fresh Ed25519 keys and a fresh set of origins, such as https://search.<tag>.relay.example. Requests between parties are real Request/Response objects routed in-process to that…

The one-line difference

  • Biscuit puts authority in the token. The clearinghouse mints a root token once. Each intermediary narrows it offline by appending blocks, and each receiver decides locally with Datalog.
  • AAuth puts authority in the person server. Every hop asks Alice's person server (PS) for a fresh, key-bound auth token. The PS decides against the mission Alice approved, and receivers verify the token and apply their own policy.

What one hop looks like

Before any work starts, the Assistant proposes a mission to Alice's PS: a description, the tools it will use and the resources it will call. Only Alice can approve it, so the PS answers 202 Accepted with an interaction URL, a code and a pending URL. The Assistant polls with signed GETs while Alice approves in her browser, then receives the mission and its mission_s256 hash. Every later token carries that hash.

Then every hop runs the same five steps:

  1. Identity call. The caller signs the request (RFC 9421) with a self-issued agent token in the Signature-Key header. The receiver now knows who is calling, and answers 401 asking for a person token.
  2. Person token. The caller asks the PS for a person token for that receiver. An intermediary passes the token it was given as upstream_token, which is how AAuth chains calls.
  3. Resource token. The caller presents the person token. The receiver answers 401 again, this time with a resource token naming the scope and billing account it needs.
  4. Auth token. The caller takes the resource token to the PS. The PS checks it against the mission and either mints an auth token or refuses.
  5. Authorized call. The receiver verifies the auth token (issuer keys, audience, expiry, and that it is bound to the key signing the request), applies local policy, and does the work.

Nothing is simulated in the protocol: the signatures, tokens and HTTP exchanges are real, routed between independent origins inside one Cloudflare Pages function. The operators, the work and the credits are simulated, as before.

Where each scenario is stopped

Scenario Biscuit lab AAuth lab
Change the payer SearchCo's Datalog compares the payer with the call The PS refuses to mint the auth token: the account doesn't match the mission's payer. SearchCo never sees a token.
Forbidden operation SearchCo's Datalog: no right("delete") The PS refuses under its own policy, even though SearchCo offers delete
Untrusted issuer Signature fails against the pinned root key Signature fails against the PS's published keys
Remove caller trust SearchCo's Datalog: trusts_caller missing SearchCo returns 403 on the first, identity-only request
Budget race, retry, failure Clearinghouse Clearinghouse, unchanged

The pattern is clear: decisions that Biscuit spreads across receivers move to the PS in AAuth.

What AAuth gives you for free

Biscuit tokens are bearer tokens, and Biscuit blocks don't record who appended them. To know who was calling, the Biscuit lab had to invent two things: a separate Ed25519 signature over every call, and signed delegation envelopes chained by hash.

AAuth has proof of possession built in. Every request is signed, every token is bound to the key that signs the request, and an intermediary signs with its own agent token. A stolen AAuth token is useless without the key. That is AAuth's clearest advantage.

AAuth also makes the person part of the protocol. Biscuit has no notion of consent; the root token simply asserts requester("alice"). In AAuth, Alice approves a mission, the PS keeps a log of every token it issues or refuses under it, and each resource sees a different pseudonymous identifier for her, so SearchCo and ComputeCo can't correlate her.

What it takes away

Offline narrowing. Biscuit's defining feature is that ResearchCo can restrict what SearchCo may do without asking anyone, and SearchCo can check that offline. AAuth has no equivalent: an intermediary can't constrain its sub-delegate. Only the PS and the receiver decide.

Caller identity, on every call. This one surprised me. In the three-party flow, no token AAuth issues names the agent. That's deliberate (Appendix C.1.6 of the draft): Alice's relationship with a resource should survive a change of agent. A receiver can refuse a caller on the identity-only request, but nothing forces a caller to make that request first. The lab has a test showing that ResearchCo can skip it, go straight to a person token, and be served by SearchCo even after SearchCo's trust in ResearchCo is removed.

So the Biscuit lab's per-receiver "I trust this caller" rule has no binding equivalent in AAuth. That policy belongs at the PS, which sees every hop.

What it costs

On one happy-path run:

Biscuit lab AAuth lab
Calls to the issuer after minting 0 16 (PS), including 2 consent polls
Recorded exchanges 13 internal evaluations 40 signed HTTP requests, 14 of them 401 challenges
Key-discovery fetches 0 (root key pinned) 23
Mean run time (Node, in-process) ~35 ms ~150 ms

Everything runs in one process, so these count messages, not network latency. In a real deployment every PS call and discovery fetch is a round trip, and the PS has to be up for any work to happen. The flip side is revocation: the PS simply stops issuing, and tokens expire within the hour. Biscuit needs revocation identifiers distributed to every verifier.

Checked against a live person server

A simulation can be wrong in ways its own tests won't catch, so I also ran AAuth's official walkthrough with a real agent identity, Hellō's person server (person.hello.coop) and a real resource (whoami.aauth.dev). The lab now has a pnpm live command that runs the Assistant's first hop both ways and prints them side by side.

The live run confirmed the order of challenges, the token lifetimes, and the caller-identity point: the resource that served Alice never learned which agent called, while the PS did. It also showed things the lab now copies, like the consent 202 and polling, and things it doesn't, like a PS that defers later requests with requirement=approval instead of deciding at once.

It also turned up two gaps in the reference JavaScript libraries, now filed upstream: exchangeToken can't send upstream_token, so call chaining can't be completed through the library, and the --explain narration doesn't always match the wire.

When to pick which

  • Pick Biscuit for offline or partition-tolerant verification, for intermediaries that must narrow authority themselves, and for one uniform policy language in tokens and at receivers.
  • Pick AAuth for proof of possession on every request, for person consent and a central audit log, and for pseudonymous per-resource identifiers.

They also combine well. AAuth can carry who (a signed agent, a person, a mission) and Biscuit can carry what (offline-attenuable rights), for example as an AAuth resource that accepts a Biscuit as its scoped grant. These labs keep them separate so the differences stay visible.

Wrapping up

The headline is simple: Biscuit decides at the edges, AAuth decides in the middle. The more interesting result is the one I didn't expect going in: AAuth's privacy goal means receivers can't enforce which agent calls them, so "who may call me" becomes the person server's job.

The full side-by-side is in COMPARISON.md. Try both labs, run the same scenario in each, and compare the Debug tabs.

Code: github.com/darkedges/aauth-lab · Demo: aauth.demos.darkedges.com · Biscuit version: biscuits.demos.darkedges.com

來源:DEV Community · dev.to