fix: quickstart hand-rolls the actor assertion the SDK now builds - #297
fix: quickstart hand-rolls the actor assertion the SDK now builds#297KunalJavelin wants to merge 2 commits into
Conversation
Section 4 signs the RFC 7523 assertion with `pyjwt.encode` and exchanges it with
`issue_token_exchange`. Both have had SDK helpers since highflame-sdk#32:
`build_actor_assertion()` builds the assertion, and `delegate_to()` builds it and
performs the exchange in one call, resolving `aud` from the client's own issuer.
Keeping the manual cells rather than replacing them — this is ZeroID's own repo
and the mechanics are the point of that section. Adds the one-call equivalent
after them, so a reader knows they do not have to hand-roll either in their own
code.
Verified the helper is a drop-in for what the notebook writes by hand:
alg build_actor_assertion=ES256 manual=ES256
iss == sub both the agent's WIMSE URI
aud issuer (string vs the manual list; server accepts both)
ttl 120s default, configurable manual 300s
extra adds a `jti` nonce the manual version omits
The claim set is exacting — `iss` must be the WIMSE URI exactly, `aud` must be
the issuer — and the server reports any mistake as an undifferentiated
`invalid_grant`, which is what makes hand-rolling it expensive.
Left alone deliberately: `localhost:8899` and the separate `ZeroIDClient` are
correct here. This notebook is a walkthrough of a locally-run ZeroID in the
service's own repo, not an SDK quickstart, so pointing it at SaaS or the unified
client would be wrong.
Refs highflame-sdk#124.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tried to test this locally, and hit a blocker that is not in this PRThere is a healthy Cause: the SDK hard-codes the admin route prefix, and this container serves the other one.
Both are legitimate — What that means for this PRThe notebook cannot execute against a local ZeroID with SDK 0.3.22 at all, with or without my change. So my added cells remain unverified by execution, and I would rather say that than imply otherwise. What is verified: The added cells use only variables the surrounding cells already define — SuggestionReasonable to merge on that basis, since the change is additive and the helpers are verified. But this notebook is unrunnable end to end for anyone following its own prerequisites until #145 is resolved — which is worth knowing regardless of this PR, and is arguably the more urgent of the two. |
Found by executing the notebook against the quickstart container, which became possible once highflame-sdk gained a configurable admin path prefix (highflame-sdk#145). `tokens.revoke(delegated_token)` in section 5 raises: RFC 7009 authenticates the *client*, so revocation needs the OAuth client credentials — which section 3 already has in scope as `oauth_client.client_id` and `client_secret`. Without them ZeroID answers `invalid_client`. `delegate_to` in the cell added earlier on this branch needs an explicit `audience`. It normally reads the issuer off the client's own token, and this notebook's client is unauthenticated; `issuer_url` is already resolved by the discovery cell. All 60 cells now run clean against `highflame-zeroid:quickstart`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It runs now — all 60 cells, against the quickstart containerhighflame-sdk#147 makes the admin path prefix configurable, which unblocked execution. Running the notebook then surfaced two more cells that could not work, both now fixed on this branch:
Section 3 already creates an OAuth client, so
VerificationMy added cells output: So OrderingThis PR is independent of sdk#147 for review, but the notebook is only runnable end to end once #147 ships — until then Two options: merge now and accept that the notebook needs #147 to run (it already did before this PR), or wait for #147 and add the |
What is stale
Section 4 hand-rolls the RFC 7523 actor assertion with
pyjwt.encode, then exchanges it withissue_token_exchange. Both have had SDK helpers since highflame-sdk#32:build_actor_assertion()— builds the assertiondelegate_to()— builds it and performs the exchange, resolvingaudfrom the client's own issuerWhat changed
The manual cells stay. This is ZeroID's own repo and section 4 is titled "Agent-to-Agent Delegation (RFC 8693)" — walking through the mechanics is the point there, and replacing them would remove the explanation. This adds the one-call equivalent immediately after, so a reader knows they do not have to hand-roll either in their own code.
Two cells, purely additive.
Verified the helper is a drop-in
Compared
build_actor_assertion()output against what the notebook writes by hand, decoding both:So it is equivalent and slightly better — the nonce guards replay, which the hand-rolled version does not.
The claim set is exacting (
issmust be the WIMSE URI exactly,audmust be the issuer) and the server reports any mistake as an undifferentiatedinvalid_grant. That is what makes hand-rolling it expensive, and worth saying in the notebook.Deliberately left alone
localhost:8899and the separateZeroIDClientare correct here, and I want to be explicit because they look like staleness at a glance:Highflameclient in the ZeroID service's own repo would be odd;ZeroIDClientis the right surface for a notebook about ZeroID.highflame-sdk#124's audit flagged these patterns, but it was auditing
highflame-sdk/examples/zeroid_quickstart.ipynb— a different, shorter notebook that was separately rewritten in332ca15a. This one covers considerably more (OAuth2 client credentials, introspection and revocation, credential policies, CAE signals) and serves a different purpose.Not fixed here, worth flagging
identities.createappears twice and works against a local instance in dev mode, but returns[403] management token not permitted on this routeagainst SaaS — verified today. Harmless for this notebook's local premise, but a reader who moves to SaaS will hit it. Happy to add a note if you want; left out to keep this change tight. Related: highflame-sdk#138.Testing
Not executed — it needs a locally-built ZeroID, which I do not have running. The added cells use the same variables the surrounding cells already define (
tool_agent,tool_agent_private_key,issuer_url,orchestrator_token), and the helper behaviour is verified above against SaaS. Worth a run by someone with a local instance before merge.Refs highflame-sdk#124.