YogaSec · IAM for AI agents

A financial-grade profile for agentic transactions

When an AI agent asks a bank to move money, who can prove what the human actually saw before saying yes?

By Ayoub Assaid, IAM architect · 22 October 2026 · about 14 minutes

In short

  • Agents now buy, transfer and pay invoices. The weak link is the human approval. Most flows today prove that a server logged what it meant to show, or that a key signed a value nobody could read.
  • Banks already know a stronger pattern. EU payment law requires the authentication code to be specific to the amount and the payee that the payer was made aware of.
  • Every building block exists, either as a standard or as a draft. What is missing is a profile that fixes the seams between them: who issues the challenge, what gets hashed, who renders it, and who verifies it.
  • I sketch that profile here, with a logical architecture and a sequence diagram. It is my proposal for discussion, not a standard.
Labels used below: Law Standard Draft Industry spec Author's proposal

01Agents are already at the till

In barely a year, card networks, wallets and identity standards bodies have all published something about agents that pay. A few dated milestones show the pace.

  • Sep 2025Google announces the Agent Payments Protocol (AP2) with signed "mandates" that record what the user approved.16
  • Oct 2025Visa launches its Trusted Agent Protocol so merchants can recognise trusted shopping agents.17 The OpenID Foundation publishes its whitepaper on identity management for agentic AI.12
  • Mar 2026Mastercard open-sources Verifiable Intent, co-developed with Google, to link "identity, intent, and action into a single, privacy-preserving record".15
  • May 2026Google and Mastercard contribute AP2 and Verifiable Intent to the FIDO Alliance.20
  • Jun 2026An IETF draft lets an API answer an agent with a signed, transaction-specific authorization challenge.7
  • Jul to Sep 2026IDPro publishes "Assembly Required" and the agent identity framework behind it is adopted by the IETF WIMSE working group as AIMS.1110
  • Sep to Oct 2026New drafts tackle the consent screen itself: a hash-pinned display document (R3) and passkey-signed permission receipts.89

Behind these announcements sit three different situations. They do not need the same proof.

A · Human present

The shopping agent you are watching

The agent fills a basket and you confirm the final cart on your phone. In AP2 this produces a Cart Mandate that your device signs. Verifiable Intent calls it the human-present mode.1615

Must prove: the exact cart and amount you confirmed.

B · Human not present

The agent with a budget

"Pay my utility bills, up to 300 € a month." You sign the rules once, then the agent acts alone. AP2 calls this an Intent Mandate.16

Must prove: the limits you signed, and that each payment stayed inside them.

C · Mid-task escalation

The agent that hits a wall

An accounts payable agent processes an invoice. The supplier's IBAN changed since last month, maybe through a forged PDF or an injected prompt. The bank API refuses and asks for a specific approval.

Must prove: that the approver saw the new beneficiary and the amount, on a channel the agent cannot reach.

Scenario C is the focus here. It is the one bank APIs face directly, and the least specified. Scenarios A and B are mostly card commerce, where AP2 and Verifiable Intent already carry signed records. When an agent calls a bank's own API, though, nothing equivalent is standardised yet.

02Three levels of proof for "the human approved"

A human approval can leave three kinds of evidence. They are not equal, and the difference is easy to miss.

1

The server says what it showed

The authorization server records which screen or document was displayed. The R3 draft does this well. The resource publishes a display document with a plain-language summary and its implications. The authorization server "MUST reject" a token if the document's hash does not match, and records r3_uri and r3_s256 in its audit log (§8.1).8

This is strong audit evidence, but it comes from the server itself. Nothing signed by the user confirms that this content reached their screen.

2

The user's key signed something

The permission receipts draft asks a passkey to sign a challenge equal to the SHA-256 hash of the permission (§4.7). The author states the limit plainly: "An approval shows that the passkey approved the action, not that the principal read it" (§6).9

Here is why. A WebAuthn authenticator signs a challenge it cannot interpret. The text on screen is drawn by an app or a web page, and that software can show one thing while the key signs another.

3

The device signed what it displayed

The component that renders the transaction is also the one that signs it, and the signed data contains the displayed fields. Two standards already work this way.

In W3C Secure Payment Confirmation, the user agent "MUST ensure" that the payee, amount and instrument are communicated to the user (§4.10.1). The payment data then goes into the client data that the authenticator signs.6 In OpenID4VP 1.0, a verifier sends typed transaction_data. The wallet returns a hash of each entry inside its signed presentation. For SD-JWT VC credentials, the SD-JWT VC appendix places these hashes in the key binding JWT.5

Law What EU payment law already asks Commission Delegated Regulation (EU) 2018/389, Article 5(1), requires that "the payer is made aware of the amount of the payment transaction and of the payee". It also requires that "any change to the amount or the payee results in the invalidation of the authentication code generated". Article 5(2)(b) adds the integrity of "the information displayed to the payer throughout all of the phases of the authentication".13

The law requires dynamic linking. It does not prescribe a protocol, and it does not require the user's device to sign the rendered text. Level 3 is the technically achievable reading that produces the cleanest evidence for it. For banks outside the EU, such as in Morocco, the RTS is not binding. It is still a precise and testable yardstick for what "the human approved" should mean.

03The building blocks already on the table

Nothing in this design needs a new protocol. Here is what exists, what each piece proves, and where it stops.

Building blockStatusWhat it providesWhere it stops
Rich Authorization Requests (RFC 9396)1 Standard Structured authorization_details describing the operation. "When gathering user consent, the AS MUST present the merged set of requirements" (§3.1). The AS MUST refuse unknown types (§5). Presentation is left to deployments (§11.2). The AS may enrich the details during consent (§7.1). No signature by the user.
FAPI 2.0 Security Profile3 with PAR (RFC 9126)2 Standard PAR is mandatory, requests are authenticated, and access tokens are sender-constrained with mTLS or DPoP (§5.3.2). RAR is only "recommended" (Note 5). No decoupled approval and no display proof.
CIBA Core 1.04 Standard Approval on a separate authentication device, outside the agent's screen. binding_message is a short code to "interlock" two devices (§7.1), not a transaction description. The client must name the user with exactly one hint.
OpenID4VP 1.0, transaction_data5 Standard Typed transaction data. Wallets that do not support it "MUST reject requests that contain it", and the wallet must error on any unrecognized type (§5.1). The hash comes back signed in the presentation. Each transaction type defines what gets displayed. Built for wallets and credentials, not yet wired to OAuth token issuance.
Secure Payment Confirmation6 W3C CR The browser displays payee, amount and instrument and signs them with a passkey. Payment fields only. Runs in a browser, which may be on the same machine a computer-use agent controls.
Transaction authorization challenge7 Draft -00 The API returns a signed JWT (txn-authz-challenge+jwt) carrying txn and authorization_details. "The access token MUST contain the txn value". "The authorization server MUST obtain any required approval" but how it does so, and how it renders the request, is left open.
AuthZEN Access Request OAuth Profile19 Draft 1 Chains the transaction challenge, Deferred Token Response and CIBA in one OpenID profile. The client presents the resource-signed challenge to the transaction_authorization_endpoint and polls it. No OpenID4VP and no signature by the user's device over the displayed transaction. A CIBA binding_message conveys the operation to the user.
AAuth R38 Draft -00 A resource-published, hash-pinned display document, logged at token issuance. Exploratory individual draft. Evidence stays at the server (level 1).
Permission receipts9 Draft -00 Passkey-signed permissions and receipts that anyone can verify offline later. Level 2 by the author's own statement. Checked after the fact, not before execution.
AP2 and Verifiable Intent1615 Industry spec Signed mandates for human-present and delegated purchases. Scoped to commerce and card rails, not to arbitrary bank APIs.
AIMS (WIMSE)10 WG draft Treats agents as workloads and maps existing specs into one model, without a new wire protocol.11 An architecture, not a banking profile.

04Why a profile, and not one more protocol

"We don't have a standards gap. We have an assembly gap."Jeff Lombardo, IDPro, 29 July 202611

That sentence describes the problem well. Each block above secures one link. The failures happen where blocks meet. A profile is the document that fixes those joins with MUST and MUST NOT, the way FAPI 2.0 turned general OAuth options into a financial-grade baseline.

Part of this assembly has already started. The AuthZEN Access Request OAuth Profile, at Draft 1, connects the transaction challenge with CIBA and Deferred Token Response.19 What it leaves out is the user's device signing what it displayed, and the API checking that signature. Four seams matter most.

Seam 1 · The challenge comes from the bank, never from the agent

If the agent builds the thing the human approves, a prompt-injected agent can build a convincing lie. In the transaction challenge draft, the API itself signs the challenge, including the operation and a transaction identifier txn. The agent only relays it, and the final token must carry the same txn.7 The agent never writes a byte the human signs.

Seam 2 · Hash what was granted, not what was asked

A natural idea is to hash the agent's request and have the user sign that hash. RFC 9396 breaks it. Deployments may "modify requested authorization details in the user consent process, e.g., adding fields" (§11.2), and the token response can return enriched details (§7.1).1 For example, the approver picks which account to debit on the consent screen. A hash of the original request no longer matches what will run.

So the object the user signs must be built after enrichment, and the token must carry the very same granted details. OpenID4VP also avoids a classic trap here. The hash is computed over the encoded transaction_data string as transmitted, so nobody has to re-serialise JSON identically to check it.5

Seam 3 · Render on a device the agent cannot drive

CIBA moves the approval to a separate device. But its binding_message is designed as "a visual cue" to link two screens, and "SHOULD be relatively short" (§7.1).4 It cannot describe a transfer, even though the AuthZEN profile notes that a binding_message can convey the operation to the user.19 Channel choice is also left to products. Auth0's asynchronous authorization, for instance, offers Guardian push and an email option where "the email contains a link" and approval "happen[s] in the browser".18

A browser or computer-use agent working in the user's own session may be able to open that inbox or that browser. That is my inference, not a documented incident. For challenged operations, the profile should allow one channel only: the bank's wallet app on a separate device, rendering typed transaction_data before it signs.

Seam 4 · Let the API check the human proof itself

Today the API trusts the authorization server's token, and the token is the only proof that someone approved. If the token also carries the user's signed presentation, the API can recompute the hash and compare it with the operation it is about to execute. A misconfigured authorization server, or a policy bug, can no longer turn a refusal into an approval without the API noticing. It also gives every party evidence they can verify independently.

05Logical architecture

Three trust zones, plus an evidence log. The agent zone is treated as compromised by design. The bank control plane issues and verifies. Only the approval zone talks to the human.

Logical architecture for agentic transaction approval The AI agent and the OAuth client sit in an untrusted execution plane. The bank API and authorization server form the bank control plane. The bank wallet on the user's phone forms an isolated approval plane. An append-only evidence log records each step. Execution plane · untrusted Bank control plane Approval plane · isolated AI agent runtime Holds only its own DPoP or WIMSE workload key Assumed injectable Confidential OAuth client mTLS or private_key_jwt Relays the challenge as is Binds the token to the agent key Bank API · resource server Signs the transaction challenge (txn) Verifies the user's proof before executing Treats each txn as single use Authorization server Verifies the API signature on the challenge Resolves the approver from policy Builds transaction_data from granted details Issues a txn-bound, sender-constrained token Bank wallet app On the user's own phone Renders every typed field Sanitises untrusted text Biometric user verification Hardware-bound key signs the transaction_data hash no approve in banner Evidence log · append-only Challenge · user proof · token jti Execution result · all linked by txn Written by the AS and the bank API 1 · 8 2 3 · relay challenge 4 5 6 7 log 1 Agent calls the payment API 2 API answers with a signed challenge 3, 4 Challenge goes unchanged to the AS 5, 6 Wallet renders, user signs 7 Token with txn and user proof 8 Agent retries, API verifies, executes Only zone that talks to the human key never leaves the phone hardware
Figure 1. Logical architecture. The agent zone is assumed compromised. The bank API and the authorization server both check the user's proof. The approval plane runs on a separate, user-held device.

06Sequence: from refusal to verified execution

The walkthrough below uses scenario C. The accounts payable agent tries to pay 18,450 € to a beneficiary whose IBAN is new. The colours show which specification each message comes from.

Sequence diagram of an agent payment approved on the user's wallet The agent calls the bank API and receives a signed transaction challenge. The client sends it to the authorization server, which pushes typed transaction data to the user's wallet. The wallet signs the hash of what it displayed. The authorization server issues a token carrying the transaction identifier and the user's proof. The agent retries and the API verifies everything before executing. AI agent OAuth client Bank API Authorization server Wallet on phone 1 POST /payments · access token + DPoP proof 2 401 · error="transaction_authorization_required" signed JWT: iss, aud=AS, txn, authorization_details, exp 3 relay challenge 4 challenge to transaction_authorization_endpoint · mTLS 5 Verify the API signature Resolve approver from policy Apply consent enrichment Build transaction_data with txn 6 transaction_authorization_id · interval (client polls) 7 push · OpenID4VP request transaction_data + nonce 8 Render every field "New beneficiary · 18,450 €" Biometric check Hardware key signs 9 vp_token KB-JWT: nonce, transaction_data_hashes 10 Check hash and nonce Write evidence for txn 11 poll with transaction_authorization_id 12 access token · txn · granted details · user proof sender-constrained to the agent key (cnf) 13 token 14 retry POST /payments · new token + DPoP 15 Verify token, DPoP and unspent txn Verify the wallet signature hash(transaction_data) is in the proof Signed details equal the operation 16 201 executed · txn marked spent
Figure 2. Steps 2, 4, 6 and 11 follow the transaction challenge draft, as also profiled by AuthZEN. Steps 7 to 9 reuse OpenID4VP 1.0 transaction data, but neither CIBA nor OpenID4VP defines the push that carries the request to the wallet. That push transport, putting the wallet's proof inside the access token (step 12), and steps 5, 10 and 15 are the glue a profile has to define.

What the wallet signs, and what the API checks

The authorization server builds one transaction_data entry after consent enrichment. The wallet decodes it, displays it, and returns its hash inside the key binding JWT that its hardware key signs. Here is the decoded entry for the invoice in scenario C:

{
  "type": "https://bank.example/tx/credit-transfer/v1",
  "credential_ids": ["bank_wallet_key"],
  "txn": "c41f9e2a-6d0b-4b8e-9a51-2f7c0d13e8aa",
  "authorization_details": [{
    "type": "https://bank.example/tx/credit-transfer/v1",
    "instructedAmount": { "currency": "EUR", "amount": "18450.00" },
    "creditorName": "Atlas Logistique SARL",
    "creditorAccount": { "iban": "FR76 3000 4000 0500 0012 3456 789" },
    "debtorAccount": { "id": "acc-ops-02" },
    "beneficiaryStatus": "new"
  }]
}

Before executing, the bank API runs checks like these (pseudo-code). None of them trusts the agent, and only the first trusts the authorization server.

# token was issued by the AS and is bound to the agent's DPoP key
verify_token(token, dpop_proof, request)

td   = token.approval.transaction_data        # exact base64url string sent to the wallet
kb   = token.approval.key_binding_jwt         # signed by the wallet's hardware key
verify_signature(kb, wallet_key_from(bank_issued_credential))
assert kb.nonce == nonce_for(token.txn)
assert b64url(sha256(td)) in kb.transaction_data_hashes

data = json(b64url_decode(td))
assert data.txn == token.txn and not spent(token.txn)
assert data.authorization_details == token.authorization_details
assert request_operation(request) within data.authorization_details

execute(request)
mark_spent(token.txn)
log_evidence(token.txn, td, kb, token.jti)

The fourth line does the real work. The API recomputes the hash of the exact string the wallet received and finds it inside a signature only the user's device could produce. If anything changes the amount or the payee afterwards, the comparison fails. That is dynamic linking, checked end to end by the party that executes.

07A profile sketch in eleven rules

Author's proposal These rules are my proposal. They are not a standard and have not been reviewed by any working group. Rules marked Existing restate what a cited specification or draft already requires. Rules marked New are what this profile adds. MUST and SHOULD express what the profile would require, not what the cited specs require today.
  1. New High-risk bank operations MUST be expressed as RFC 9396 authorization_details with a registered, versioned type.Tightens FAPI 2.0 Note 5, where RAR is only recommended.
  2. Existing A challenged operation MUST start with a challenge signed by the resource server. The agent MUST NOT construct any data that the user signs.Transaction challenge draft, §3. The second sentence makes its intent explicit.
  3. Existing The authorization server MUST verify the challenge signature and determine the approver from policy, not from a hint supplied by the client.Transaction challenge draft, step 6, against CIBA's client-supplied hint.
  4. New The approval object MUST be built from the granted details after consent enrichment and MUST include txn.Answers RFC 9396 §7.1 and §11.2.
  5. New Exactly one transaction_data entry MUST be sent per challenge, hashed with SHA-256 only.Avoids the hash-matching ambiguity raised in OpenID4VP issue #442.21
  6. New Approval MUST happen on a device separate from the agent runtime, in the bank's app, with a hardware-bound key and user verification. Email links and same-device redirects MUST NOT be used for challenged operations.Extends CIBA's decoupled model.
  7. New The wallet MUST display every field of the type before signing and MUST NOT offer approval from a notification banner. It MUST also reject unknown types, which OpenID4VP already requires.OpenID4VP §5.1 for unknown types. Display rules are new.
  8. New Strings that come from outside the bank, such as creditor names or remittance text, MUST be shown as plain data, without markup or bidirectional control characters.Profile rule. RFC 9396 §2.1 already warns about ambiguous type values.
  9. New The access token MUST carry txn, the granted details and the user's proof, and MUST be sender-constrained to the agent's key.The txn binding comes from the transaction challenge draft and sender constraint from FAPI 2.0 §5.3.2.1. Carrying the user's proof is new.
  10. New The resource server MUST verify the user's proof itself and MUST treat txn as single use.Profile rule implementing RTS Article 5(1)(d) end to end.
  11. New Below a risk threshold, a pre-signed mandate with limits SHOULD replace per-action approval, so the human is asked rarely and only for what matters.Same logic as AP2 Intent Mandates and Verifiable Intent's autonomous mode.

08What this does not solve

  • Signing is not reading. Level 3 proves what was displayed, not what was understood. For high-risk AI systems, the EU AI Act asks that overseers "remain aware of the possible tendency of automatically relying or over-relying" on system output (Article 14(4)(b)).14 If approvals become frequent, people will tap through them. Rule 11 matters as much as the cryptography.
  • A compromised phone. A hardware key protects the key, not the screen. Malware with screen overlay rights on the phone can still mislead the user. App attestation narrows this risk without removing it.
  • Who defines the display. Each transaction type needs a reviewed rendering. A published, hash-pinned display document in the style of R3 is a good candidate for this, as long as the wallet signs over it and the server does not just log it.
  • Reach and cost. It only works where the approver has the bank's app. Corporate dual control means several approvers and several proofs per txn.
  • Moving ground. The transaction challenge and R3 are -00 drafts, and the AuthZEN profile is at Draft 1. All of them will change. As of 22 October 2026, I found no FAPI or OpenID Foundation profile that adds a signature by the user's device over the displayed transaction, verified by the API. If one appears, this sketch should be compared with it.

09Where this belongs

The natural home is the OpenID Foundation's FAPI work, which already maintains FAPI 2.0 and a CIBA profile for financial services. The transaction challenge lives in the IETF OAuth community, and the AuthZEN profile already chains it with CIBA. Three concrete contributions seem more useful than a new draft. The first is a comment on the transaction challenge draft, asking how approval evidence can reach the resource server. The second is a comment on R3, asking for an option where the user's device signs the display and does not just log it. The third is proposing to the AuthZEN profile an approval option based on OpenID4VP transaction data.

If you work on these drafts, run agent payments at a bank, or see where this sketch breaks, I would like to hear it. YogaSec is a personal project shared with the community, and this post is open to correction.

Update, 25 October 2026. Sections 01 and 07 use the mandate names AP2 had when it launched in September 2025: Cart Mandate and Intent Mandate. The current AP2 specification defines a Checkout Mandate (what is bought) and a Payment Mandate (how it is paid).2220 Each starts Open, holding the user's constraints, and becomes Closed when bound to a final cart and amount. In case A the user closes it, in case B the agent does, within the limits the user signed. The argument is unchanged.

Sources

  1. IETF, RFC 9396, OAuth 2.0 Rich Authorization Requests, May 2023. §2.1, §3.1, §5, §7.1, §11.2.
  2. IETF, RFC 9126, OAuth 2.0 Pushed Authorization Requests, September 2021.
  3. OpenID Foundation, FAPI 2.0 Security Profile, Final, 22 February 2025. §5.3.2.
  4. OpenID Foundation, OpenID Connect CIBA Core 1.0. §7.1.
  5. OpenID Foundation, OpenID for Verifiable Presentations 1.0, Final, 9 July 2025. §5, §5.1, §8.4.
  6. W3C, Secure Payment Confirmation, Candidate Recommendation Draft, 13 October 2026. §1.1.1, §4.10.1.
  7. Rosomakho, Campbell, McGuinness, Kasselman, draft-rosomakho-oauth-txn-challenge-00, OAuth Transaction Authorization Challenge, June 2026.
  8. Hardt, draft-hardt-aauth-r3-00, AAuth Rich Resource Requests, 28 September 2026. §8.1.
  9. Izmaylov, draft-izmaylov-agent-permission-receipts-00, October 2026. §4.7, §6.
  10. IETF WIMSE WG, draft-ietf-wimse-aims, AI Identity Management System, September 2026.
  11. Jeff Lombardo, Assembly Required: How we Stopped Reinventing and Started Composing Standards for Agentic Identity, IDPro, 29 July 2026.
  12. OpenID Foundation, Identity Management for Agentic AI, arXiv:2510.25819, October 2025.
  13. European Commission, Delegated Regulation (EU) 2018/389, RTS on strong customer authentication, Article 5.
  14. European Union, Regulation (EU) 2024/1689, Artificial Intelligence Act, Article 14.
  15. Mastercard, Verifiable Intent, 5 March 2026.
  16. SiliconANGLE, Google's Agent Payments Protocol provides framework for AI to automate consumer purchases, 16 September 2025. Mandate types described in Vellum, Google's AP2.
  17. FStech, Visa launches protocol for agentic commerce, 15 October 2025.
  18. Auth0, Asynchronous Authorization, product documentation, consulted 22 October 2026.
  19. OpenID AuthZEN WG, Karl McGuinness, AuthZEN Access Request OAuth Profile, Draft 1, consulted 22 October 2026.
  20. FIDO Alliance, Building the trust layer for agentic payments with AP2 and Verifiable Intent, 26 May 2026.
  21. OpenID DCP WG, OpenID4VP issue #442, matching transaction_data_hashes to transaction_data entries.
  22. Google, Agent Payments Protocol (AP2) specification, Checkout Mandate and Payment Mandate, consulted 25 October 2026.
YogaSec is a personal project on identity and access management for AI agents, shared with the community. The views expressed here are personal. Status labels and quotes were checked against the sources listed above on 22 October 2026.

Go further