Repository navigation
fix: draft-11 live test, person-bound demo roles, spec-compliant interaction chaining - #55
Merged
Merged
Conversation
- Mode 2b now expects requirement=person-token for a scoped request carrying only an agent token (draft-11 issues the resource token after a person token), so Mode 3 is no longer skipped. - Use a live egress policy admitting person.hello.coop's cross-origin jwks_uri (issuer.hello.coop) instead of the localhost-only SampleEgress. - Report token verification failures in Mode 3 instead of crashing. Mode 3 remains blocked upstream: whoami.aauth.dev still emits person_token_jti instead of the required presented_jti (aauth-dev/whoami#5). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
samples/README.md changed in the previous commit, so the frozen docs inventory hash was stale. AGENTS.md records which files the inventory covers and how to regenerate the snapshot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Roles are identity claims about the person (RFC 9068/SCIM), so the demo roles no longer depend on a hard-coded agent ID that the Agent Provider can no longer assign (IDs are now aauth:agent-<hash>@host): - MockPersonServer asserts calendar.owner, wallet.payer and demo-users for its demo person whichever agent asks. MockPersonServer:GuestPerson=true acts for a person with no roles, to exercise role-based denial. - The Federated stub AS no longer derives roles from the agent ID. For wallet.charge it asks the PS for the person's roles via requirement=claims, and denies when the pushed claims lack wallet.payer. - Keycloak tests now decide by the logged-in user, like the real realm. A denied deferred request now keeps the server's reason: the PS->AS client keeps the AS problem detail, the PS relays it on its own `denied`, and agents include it in AAuthInteractionDeniedException. Display fixes: - PS dashboard and consent pages show an R3 request's r3_uri instead of the PS default scope (calendar.read), which R3 tokens don't carry. - EventAgent prints each consent URL once instead of an extra empty "Consent:" line. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AgentConsole prints its AP-assigned agent ID. /admin/consent keys consent on that ID plus the agent's key thumbprint, so the docs now pass both instead of the old aauth:demo@ap.example label. - An explicit trailing "/" now targets the resource root. Before, the documented http://localhost:5200 Concierge call was sent to /events and returned 404. - PS flows now follow a resource's 202 + requirement=interaction, which the Concierge uses to relay downstream consent. - The Concierge README documents the two consent hops. Pre-granting the second hop isn't practical because the Concierge generates a new key on every start. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…g them Interaction chaining aborted the intermediary's downstream token exchange on the first 202 and re-ran the whole chain on every caller poll. Each re-run sent a new token request to the PS, so one AgentConsole run through the Concierge left ~80 duplicate consent requests. Draft-11 §Interaction Chaining: the intermediary "completes the original request and returns the result at its pending URL" once it obtains the downstream token. §Polling with GET: after a 202 the agent "switches to GET for all subsequent requests to the Location URL and does not resend the original request". - AAuthChainedOperation<TResult> runs the downstream work in the background. Its per-request interaction handler records the downstream interaction and returns normally, so the SDK keeps polling the downstream Location with GET. It publishes versioned interaction snapshots, cancels on expiry, host shutdown or Cancel(), and observes failures nobody awaits. - AAuthRequestOptions.UpstreamToken passes the upstream token per request, so downstream work no longer needs the inbound HttpContext. - AAuthChainedInteractions: Park(Interaction), Rekey for a new downstream step (new code, same id/Location), and PollingFailure mapping downstream outcomes to §Polling Error Codes. - A request with its own interaction handler no longer joins another request's in-flight token acquisition, which would never call its handler. - Concierge (/, /mission, /wallet): polls read the operation's state and never re-run the chain. Entries re-key atomically, earlier codes stay valid, and DELETE cancels the downstream work. - Docs, SampleApp CallChain snippet and GuidedTour text describe the new flow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
make live(LiveWhoAmITest against whoami.aauth.dev + person.hello.coop) had drifted from draft-11 and never reached the Person Server.Changes
requirement=person-token. Under draft-11 a resource answers a scoped request carrying only an agent token withperson-token. It issuesauth-token+ resource token only after it sees a person token. The old check expectedauth-tokendirectly, so 2b failed and Mode 3 was skipped.IsAuthTokenChallengeis replaced byIsPersonTokenChallenge, with tests updated to match.jwks_urionissuer.hello.coop. The sample now uses its ownAAuthEgressPolicywithcrossOriginJwks: [(person.hello.coop, issuer.hello.coop)]instead of the shared localhost-onlySampleEgress.TokenVerificationExceptionis caught and reported, so the run prints the PASS/FAIL summary instead of crashing with an unhandled exception.samples/README.mdand the Mode 3 flow text now describe the draft-11 sequence.DocumentationInventory.snapshot.mdfor the README change, and addedAGENTS.mddescribing when and how to regenerate it.Live result
Mode 3 first failed because whoami's resource token lacked the draft-11
presented_jticlaim (aauth-dev/whoami#5). That is now fixed upstream, and the full three-party flow passes against the live deployments with no SDK change.Validation
dotnet test tests/AAuth.Tests --filter LiveInteropValidationTests: 21 passedmake liveagainst whoami.aauth.dev + person.hello.coop: all four modes passConsole samples: role binding and consent display
Running every console sample against
make demoshowed that AgentConsole's RBAC (/events/admin) and payment (/wallet/charge) calls always failed. The PS and the stub AS granted demo roles only to the hard-coded agentaauth:demo@ap.example, but MockAgentProvider now assigns IDs likeaauth:agent-<hash>@localhost, so a real agent could never match.Roles now belong to the person (spec-aligned).
rolesandgroupsare identity claims about the user (RFC 9068/SCIM).calendar.owner,wallet.payeranddemo-usersfor its demo person whichever agent asks.MockPersonServer:GuestPerson=trueacts for a person with no roles, to show role-based denial.wallet.chargeit asks the PS for the person'sroles(requirement=claims) and denies ifwallet.payeris missing.Display fixes
calendar.read(the PS default scope) for R3 requests, which carryr3_uriinstead ofscope. They now show the R3 request.Consent:line.Verified live against
make demowith an AP-assigned agent ID:/events/admin→ 200, withrolesin the token./wallet/charge→ 200: PS consent → AS claims push → AS consent.GuestPerson=true:/events/admin→ 403, and/wallet/chargeis denied with the policy reason.All suites pass: 3,695 tests.
Interaction chaining: poll the downstream instead of re-sending it
Concierge aborted its downstream token exchange on the first
202and re-ran the whole chain on every caller poll. Each re-run sent a new PS token request, so one AgentConsole run left ~80 duplicate consent requests on the PS dashboard. That was the SDK's documented pattern.Draft-11 §Interaction Chaining says the intermediary "completes the original request and returns the result at its pending URL". §Polling with GET says that after a
202the agent "switches to GET … and does not resend the original request". So the intermediary must keep its downstream request and poll it.AAuthChainedOperation<TResult>keeps the downstream exchange alive in the background and lets the SDK poll the downstreamLocationwith GET. Also addsAAuthRequestOptions.UpstreamToken,AAuthChainedInteractions.Rekey/PollingFailure, and aPark(Interaction)overload. A request with its own interaction handler no longer joins another request's in-flight token acquisition.DELETEcancels.docs/advanced/interaction-chaining.md, the SampleApp CallChain snippet and GuidedTour text.Verified live: Agent → Concierge → Calendar left exactly one pending PS request per hop through ~30 s of agent polling, then returned 200. Agent → Concierge → Wallet (PS + AS + chained consent) returned 200 with no pending requests left. A denied downstream hop returned
403 deniedwith the PS's reason. A code review pass found three issues (same-token concurrent requests hanging, a snapshot race, far-future expiry throwing); all are fixed with tests.Tests: 3,711 pass.