Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion examples/zeroid_quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,20 @@
"outputs": [],
"source": "delegated_response = client.tokens.issue_token_exchange(\n subject_token=orchestrator_token, # orchestrator's active token\n actor_token=actor_assertion, # tool agent's self-signed assertion\n scope=\"data:read\", # requested scope (must be subset of both)\n)\n\ndelegated_token = delegated_response.access_token\n\nprint(f\"Delegated token issued:\")\nprint(f\" Token Type: {delegated_response.token_type}\")\nprint(f\" Scope: {delegated_response.scope}\")\nprint(f\" Expires In: {delegated_response.expires_in}s\")\nprint(f\" Token: {delegated_token[:60]}...\")"
},
{
"cell_type": "markdown",
"id": "61cca6a6",
"metadata": {},
"source": "### The same thing in one call\n\nThe two cells above are what delegation *is* — a self-signed assertion, then an\nRFC 8693 exchange — and they are written out here because this is ZeroID's own\nrepo and the mechanics are the point.\n\nIn your own code you do not need to hand-roll either. `build_actor_assertion()`\nbuilds the assertion, and `delegate_to()` builds it *and* performs the exchange.\n\nThe claim set is exacting — `iss` must be the WIMSE URI exactly, `aud` must be\nthe ZeroID issuer — and the server reports any mistake as an undifferentiated\n`invalid_grant`, so the helpers are worth using."
},
{
"cell_type": "code",
"id": "de37091f",
"metadata": {},
"execution_count": null,
"outputs": [],
"source": "from highflame.zeroid import build_actor_assertion\n\n# Equivalent to the pyjwt.encode cell above: ES256, iss == sub == the WIMSE\n# URI, aud == the issuer. It also adds a `jti` nonce, which the manual version\n# above omits.\nassertion = build_actor_assertion(\n wimse_uri=tool_agent.wimse_uri,\n private_key_pem=tool_agent_private_key,\n audience=issuer_url,\n)\n\n# Or skip both steps. delegate_to() signs the assertion and exchanges it,\n# resolving `aud` from the client's own issuer.\none_call = client.tokens.delegate_to(\n wimse_uri=tool_agent.wimse_uri,\n private_key_pem=tool_agent_private_key,\n scope=\"data:read\",\n subject_token=orchestrator_token,\n # This client is unauthenticated, so delegate_to cannot read the\n # issuer off its own token. Pass it — it is the `aud` the assertion\n # needs, and the discovery cell above already resolved it.\n audience=issuer_url,\n)\n\nprint(f\"assertion built: {assertion[:40]}...\")\nprint(f\"delegated in one call, scope: {one_call.scope}\")"
},
{
"cell_type": "markdown",
"id": "cell-s4-claims-md",
Expand Down Expand Up @@ -701,7 +715,7 @@
"metadata": {},
"outputs": [],
"source": [
"client.tokens.revoke(delegated_token)\n",
"# RFC 7009 authenticates the *client*, not the token, so revocation needs\n# the OAuth client credentials from section 3. Without them ZeroID answers\n# invalid_client. To revoke an agent without an OAuth client, use\n# agents.deactivate(), which collapses every token delegated from it.\nclient.tokens.revoke(\n delegated_token,\n client_id=oauth_client.client_id,\n client_secret=client_secret,\n)\n",
"print(\"Token revocation request sent (RFC 7009 — always returns 200).\")"
]
},
Expand Down
Loading