You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Proposes a shared ConfirmationAPI/ConfirmationUI mechanism for guaranteeing real human consent (T&Cs acceptance, purchase confirmation, other high-risk operations) rather than relying on LLM agent prompting alone.
Defines the core concepts (ConfirmationRequest, Agreement, ConfirmationSeeker, ConfirmationAPI, ConfirmationUI), their fields, and the separation of OAuth-based agent access from IDP-based human access.
Includes diagrams: an actor/interface diagram, a Status lifecycle state diagram, and sequence diagrams for the approval path and the reject/cancel/expire paths.
Documents rejected alternatives (OS-level popups, relying on agent prompting).
Refined during review
Copilot's review surfaced several real gaps beyond the initial draft, addressed in this PR:
CustomerID/SeekerID must be bound to the caller's authenticated identity, not merely asserted, for both creation and status/cancel calls.
Approval must be bound to a specific operation (immutable identifier/digest), and treated as single-use via a mandatory atomic claim - both at approval-consumption time and at request-creation time (idempotent create) - so retries or concurrent pollers can't execute an approved operation twice.
Status transitions (including expiry) must be an atomic compare-and-set, not a blind write.
TERMS_AND_CONDITIONS agreement data must capture an immutable snapshot/hash of what was accepted, not just a live URL.
ConfirmationUI needs a documented read operation, must treat seeker-supplied content as untrusted (XSS), and must have CSRF and clickjacking (framing) protection.
The "only direct human interaction" claim is scoped to "IDP-authenticated as the customer" rather than proof of live human presence, which is called out as future work.
Two further refinements on the atomicity/idempotency theme (making the lazy expiry-on-read update itself conditional/atomic, and showing the seeker invoking the atomic claim in the approval flow) were raised in a final review round and are left open for human review rather than folded in automatically - see PR comments.
Test plan
Docs-only change; no build/test required
Mermaid diagrams render correctly in GitHub's markdown preview
Proposes a shared ConfirmationAPI/ConfirmationUI mechanism for
guaranteeing real human consent on high-risk operations (T&Cs,
purchases) that agents cannot satisfy through prompting alone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clarifies CustomerID/SeekerID authorization binding, requires atomic
compare-and-set status transitions and lazy expiry enforcement, binds
approval to a specific operation, captures immutable terms snapshots
instead of live URLs, documents ConfirmationUI's read access, and
scopes the human-presence claim to IDP-authenticated identity rather
than proof of live human interaction.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Missing CSRF protection on state-changing confirmation requests
docs/proposals/consent.md:118
Because ConfirmationUI is an HTML page acting with an IDP-authenticated customer session, a third-party site could forge the approve/reject POST if the session is cookie-based. That would allow a confirmation to be recorded without the customer intentionally interacting with this UI, so the proposal should require CSRF protection (for example, an anti-CSRF token plus Origin/Referer validation, with appropriate cookie settings) on these state-changing calls.
Requires ConfirmationUI to treat seeker-supplied Description/AgreementData
as untrusted (contextual escaping, data-only schema, URL allowlist) and
to protect the approve/reject call with anti-CSRF measures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixed in 417eace (addressing the 'Previously missed' CSRF finding, which has no separate review thread to resolve): added a requirement that the approve/reject call be protected with anti-CSRF measures (anti-CSRF token + Origin/Referer validation) so a third-party site can't forge an approval/rejection on the customer's authenticated session.
… identity checks
Approval must be treated as single-use/idempotent rather than a durable
signal a seeker can act on repeatedly, and the ConfirmationAPI - not
just the ConfirmationUI's client-side check - must independently
validate the IDP principal against CustomerID on every relevant call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fold ExpirationDate into the same compare-and-set predicate as the
status check, closing a race where an approval could read REQUESTED
just before expiry and win the transition just after it. Also
strengthen approval-reuse protection from a recommendation into a
mandatory, atomically-claimed single-execution requirement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The atomic single-claim mechanism only protects one existing token;
without idempotent creation keyed by the operation identifier, a
retried create call could mint two tokens for the same operation,
each independently approvable and claimable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
**Status** follows a simple lifecycle. `APPROVED`, `REJECTED`, `CANCELLED`, and `EXPIRED` are all terminal; a **ConfirmationRequest** cannot be re-approved, re-rejected, or reopened once it leaves `REQUESTED`.
Each transition out of `REQUESTED` must be applied as an atomic compare-and-set against the persisted status, not a blind write: an approve, reject, or cancel only succeeds if the stored status is still `REQUESTED`, and a request that loses that race receives back whichever terminal status was already recorded. This prevents, for example, an approval and a cancellation racing to overwrite one another. There is no separate "expire" operation in the **ConfirmationAPI**; instead, `ExpirationDate` is part of that same atomic predicate rather than a separate lazy check layered on top - an approve or reject only succeeds if the stored status is `REQUESTED` **and** `ExpirationDate` has not yet passed, evaluated together in the one transition. This closes the race where an approval reads `REQUESTED` just before expiry but would otherwise win the status-only compare-and-set just after it: any transition attempt (or plain read) against a `REQUESTED` request whose `ExpirationDate` has already passed instead persists and returns `EXPIRED`.
Stopping the automated Copilot review loop here per author decision, after 7 rounds. Six rounds surfaced genuine, progressively-deeper concurrency/security requirements that were fixed (CustomerID/SeekerID authorization binding, operation binding, atomic status transitions, expiry-atomicity, idempotent approval consumption, idempotent creation, clickjacking protection). This final round raised two more in the same vein that are left open for human review rather than auto-fixed:
Both are legitimate refinements of points already in the doc, but the review has reached a point of diminishing, increasingly implementation-level returns for what is meant to be a design proposal rather than a full spec. CI is green (cicd, Analyze, Conventional commit, drift all passing).
The reason will be displayed to describe this comment to others. Learn more.
I like this idea a lot. Been reading this for a while and wanted to leave some ideas/questions. Most of them out of curiosity but others are related to the design:
This is future-idea but a hook/event handling from cli -> developer portal mobile app could resolve the push notifications. Something similar to claude /remote which gives the notification of an approval needed for an agent on the app.
Security wise I think the expiration is strong, but It'll be good to also consider having a max number of open confirmations, otherwise a really elaborate (but plausible) scenario could spam tons of "similar" confirmations so the human behind can fall in "this kind of looks like It?" and confirm. Similar to MFA spam attacks.
Curious on If reseller or teams account use the cli how would confirmation delegation work in that case or If that's going to be even an option?
Unsure If this could be a valid scenario, but in the case there's several confirmations needed for an action (something requiring T&C and pay in a single go) I see fit a multi-step confirmation where these 'perms' can be checked and then confirmed.
I know the doc is not meant to cover that but curious on If there's a base idea on how the AuditData would be handled? I think some of that is PII
One extending idea (which could be already inferred from the doc) is that a confirmation should be immutable once created and pending (And I think through the whole process), so there's no way to mid-catch and modify It somehow before approval (approving something that changed on the fly)
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
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
ConfirmationAPI/ConfirmationUImechanism for guaranteeing real human consent (T&Cs acceptance, purchase confirmation, other high-risk operations) rather than relying on LLM agent prompting alone.ConfirmationRequest,Agreement,ConfirmationSeeker,ConfirmationAPI,ConfirmationUI), their fields, and the separation of OAuth-based agent access from IDP-based human access.Statuslifecycle state diagram, and sequence diagrams for the approval path and the reject/cancel/expire paths.Refined during review
Copilot's review surfaced several real gaps beyond the initial draft, addressed in this PR:
CustomerID/SeekerIDmust be bound to the caller's authenticated identity, not merely asserted, for both creation and status/cancel calls.TERMS_AND_CONDITIONSagreement data must capture an immutable snapshot/hash of what was accepted, not just a live URL.ConfirmationUIneeds a documented read operation, must treat seeker-supplied content as untrusted (XSS), and must have CSRF and clickjacking (framing) protection.Two further refinements on the atomicity/idempotency theme (making the lazy expiry-on-read update itself conditional/atomic, and showing the seeker invoking the atomic claim in the approval flow) were raised in a final review round and are left open for human review rather than folded in automatically - see PR comments.
Test plan
🤖 Generated with Claude Code