Agentic identity · Hosted agents

A Refund Agent That Never Holds
the Database Keys

Big Blue Returns runs inside IBM Consulting Advantage Agent Studio, reaches an enterprise MCP server through ContextForge, and completes a refund with a credential that exists for five minutes. The agent decides. Identity and policy define what it can actually do.

Robert Graham / Global Product Architect, IBM Verify / September 2026
A secure Big Blue Returns refund flow in which a signed-in user, IBM Verify, an ICA-hosted agent, ContextForge MCP Gateway, an enterprise MCP server, HashiCorp Vault, and PostgreSQL exchange narrowly scoped authorization and short-lived credentials.
One refund, end to end. Read it left to right: a person signs in, the browser stages a two-minute reference, the hosted agent carries only that reference, ContextForge routes the governed call, and everything with real authority happens on the enterprise side.

The picture above is the whole argument, so here is how to read it. A customer signs in to the Big Blue Returns site through IBM Verify. The site stages that person's token with our MCP server and gets back a two-minute sessionRef. The agent running inside IBM Consulting Advantage uses that reference as an ordinary tool argument. ContextForge MCP Gateway governs the path from the agent to our tools. The enterprise MCP turns the reference back into real user context and builds a precisely scoped authorization request. IBM Verify issues an on-behalf-of token. Vault mints a Postgres credential that lives five minutes. The refund completes, the lease is revoked, and the system keeps evidence of every stage.

The interesting part was not teaching an agent how to issue a refund. It was proving that the hosted runtime never needed a permanent database credential to do it.

The thesis in four sentences

The agent makes the decision. The runtime makes the request. Identity determines the authority. Vault releases a credential only when the request is specific enough to deserve one.

01 Hosted agents do not need standing authority

Most of the agent deployments I get asked to look at share one habit. Somewhere near the agent sits a credential with more reach than any single action needs: a service account, a connection string, an API key with write scope. The agent is trusted with it because the agent has to do things, and the credential is the easiest way to let it.

That habit is where the risk lives. A prompt-injected agent with a standing credential can do anything the credential can. A compromised runtime with a standing credential is a compromised database. The credential does not care who asked.

So the design goal for Big Blue Returns was narrow. I did not want to make the agent more trusted. I wanted to make its authority smaller, more explicit, and easier to prove. The agent should be able to decide that a refund is warranted and ask for it. Whether the refund is allowed, for whom, for how much, and with what database access should be decided somewhere the agent cannot reach, on the strength of the signed-in user's identity, and enforced with a credential that does not outlive the request.

This post walks through the build that proves that is achievable with a hosted agent platform, not only with an agent you run yourself.

02 What Big Blue Returns is

Big Blue Returns is a refund desk. A customer chats with it, gives it a receipt number, and it looks the receipt up, verifies the customer who owns it, and processes the refund. Nothing exotic. It is deliberately a boring business action, because boring business actions are where these patterns have to hold.

The agent itself, named bigblue-returns, runs inside IBM Consulting Advantage Agent Studio. It is a Strands orchestration on GPT-5.1, defined in a YAML file (refund-ica/ica/bigblue-returns-agent.yaml) and imported through the Agent Studio console. From the outside it is reachable through one path, the A2A protocol, which is how the branded Big Blue Returns web application talks to it.

What the agent does not have is a database. It has three tools, exposed through ContextForge MCP Gateway, and each tool call has to earn its access on the enterprise side.

Where the pattern comes from

This is the IBM Agentic Identity runtime-security pattern applied to a hosted agent: IBM Verify for identity and authentication, OIDC Authorization Code Flow with PKCE for sign-in, RFC 8693 token exchange for delegation, RFC 9396 Rich Authorization Requests to make the delegation specific, the act claim to name the acting agent, and HashiCorp Vault with the verify-rar plugin to turn all of that into a credential that expires. Earlier posts on this site build the same chain around agents I host myself. This one puts the agent on someone else's platform and keeps the chain intact.

03 The security property we wanted to prove

Stated as a single sentence: a hosted agent does not need standing database access to complete a business action. It can act only on behalf of a real signed-in user, for one specific authorized action and amount, using a narrowly scoped credential that exists for five minutes and is revoked after use.

Every clause in that sentence maps to something you can see. "Real signed-in user" is the sub claim in the on-behalf-of token, and it is the person who logged in, not an integration account. "One specific action and amount" is the RFC 9396 authorization detail carried in that token. "Narrowly scoped credential" is a Postgres role Vault mints for the retail schema. "Five minutes" is the lease TTL. "Revoked after use" is a revocation call the MCP makes on both the success and the failure path, and the audit receipt records whether it ran.

I wanted each of those to be observable in the running system, not asserted in a diagram. Section 09 covers the evidence. The rest of the post explains how the pieces fit.

04 ContextForge MCP is the governed integration seam

Here is the separation that makes the whole thing work. The agent runtime decides which tool it needs. ContextForge decides how that tool call reaches the enterprise. The enterprise MCP decides whether the call is authorized and what credential, if any, it gets. Three parties, three jobs, and none of them has to do another one's job.

Concretely, ContextForge MCP Gateway composes our MCP's tools into the ICA application's virtual server, so the agent sees lookup-receipt, lookup-customer, and process-refund as ordinary tools it can call. When the agent calls one, ContextForge routes the governed invocation to the enterprise MCP endpoint and authenticates to that endpoint with its configured bearer-token mechanism. It carries the tool call across the boundary. That is its role, and it is a valuable one, because it means the hosted agent never needs direct connectivity to a database or broad database credentials. It needs a governed connection point.

I want to be precise about what happens on each side of that seam, because it is easy to blur. ContextForge does not perform the RFC 8693 exchange, enforce the RFC 9396 authorization details, or mint the Vault credential. Those run inside the enterprise-controlled MCP service, which stays the identity-aware policy enforcement and credential-release boundary. ContextForge's contribution is to make a hosted agent's tool call arrive at that boundary in a governed, composable way. The integration is useful because it preserves a clean separation: the agent can ask, ContextForge can route, and the enterprise authorization layer can decide.

Why the separation is the point

The pattern does not require every agent runtime to become an identity provider, a secrets manager, or a privileged database client. It requires a governed connection point and an enterprise policy service that can turn an agent request into an identity-bound, action-specific authorization decision.

That is also why the same ContextForge integration model is not specific to refunds. Any governed enterprise MCP service, API, system of record, or security decision point that can sit behind that seam can be reached the same way, with the same division of responsibility. I come back to this in section 11.

05 A refund, end to end

This is the full sequence for one refund. It reads long because every step is a distinct control, and I would rather show the whole chain than summarize it.

  1. A user signs in to the branded Big Blue Returns UI through IBM Verify.
  2. Authentication uses OIDC Authorization Code Flow with PKCE.
  3. The UI stages the user's real access token at the enterprise MCP with POST /session/stage.
  4. The MCP returns an opaque, unguessable, two-minute sessionRef.
  5. The UI injects the sessionRef into the A2A message it sends the ICA agent using message/stream.
  6. That A2A call is authenticated with an application-scoped ak_ key.
  7. The ICA agent decides which refund tool to invoke and passes the sessionRef unchanged as a tool argument.
  8. ContextForge MCP Gateway, registered as bigblue-returns-ars, routes the tool call to the enterprise MCP endpoint at ${MCP_ENDPOINT}.
  9. ContextForge authenticates to that endpoint using its configured bearer-token mechanism.
  10. The enterprise MCP resolves the sessionRef back to the original user token.
  11. The MCP fails closed if the reference is missing, invalid, expired, or cannot be bound to the request.
  12. The MCP runs an RFC 8693 token exchange at IBM Verify.
  13. The exchange request carries RFC 9396 authorization_details that identify the precise refund action and the authorized amount.
  14. IBM Verify issues an on-behalf-of token.
  15. That token includes an act claim identifying the Big Blue Returns agent as the acting workload.
  16. The enterprise MCP presents the on-behalf-of token to HashiCorp Vault's verify-rar plugin.
  17. The plugin validates the RAR authorization constraints.
  18. Vault mints a narrowly scoped Postgres role for the retail schema.
  19. The credential lasts five minutes.
  20. The refund executes using the temporary credential.
  21. The Vault lease is revoked after use.
  22. The audit trail captures the subject context, the on-behalf-of context, the authorization decision, the Vault lease, and the revocation outcome.
End-to-end architecture. Top row: a customer signs in through IBM Verify to the Big Blue Returns UI, which stages a two-minute sessionRef and sends an A2A message to the Big Blue Returns agent in ICA Agent Studio. Bottom row: ContextForge MCP Gateway routes the tool call to the enterprise MCP server, which performs RFC 8693 token exchange with RFC 9396 authorization details at IBM Verify, presents the on-behalf-of token to HashiCorp Vault verify-rar, receives a five-minute Postgres credential for the retail schema, and records the whole chain in the ARS pipeline and audit receipts.
The hosted side decides and asks. The enterprise side authorizes and releases. ContextForge is the seam between them, and the only thing that crosses it from the agent is a two-minute reference.

06 The sessionRef pattern: carrying context without handing the agent a token

The question I kept coming back to was how the enterprise MCP should learn who the signed-in user is, given that the agent sits on a platform I do not run. The obvious answer, put the user's access token in the message and let the agent forward it, is the one I did not want. A bearer token in a prompt or a tool argument is a bearer token in every log along the way, and it hands the agent something it can replay.

So the token never travels with the agent. The browser session, which already holds it, stages it directly with the MCP over a private path and receives a reference in return. The reference is what the agent sees. It looks like this on the wire.

// A2A message from the Big Blue Returns UI to the ICA agent (sanitized)
{
  "message": "Please refund order 84721 for $42.50.",
  "context": {
    "sessionRef": "${SESSION_REF}"
  }
}
    a2a · message/stream

The reference is opaque and short-lived. It carries no identity of its own and it means nothing to IBM Verify, to Vault, or to any external API. It is useful to exactly one party, the enterprise MCP, which holds the matching staged token for two minutes. The agent forwards it as a tool argument and does not need access to the original user token to do its job.

When the agent decides a refund is warranted, the tool call it makes through ContextForge carries the business intent and the reference, nothing more.

// MCP tool invocation as it arrives at the enterprise MCP (sanitized)
{
  "tool": "issue_refund",
  "arguments": {
    "orderId": "84721",
    "amount": { "currency": "USD", "value": "42.50" },
    "sessionRef": "${SESSION_REF}"
  }
}
    mcp · tools/call

Read that as a request for authorization, not as proof of it. Nothing in the tool call grants anything. The MCP resolves the reference, and if the reference is missing, expired, or does not match a token it staged, the call is refused. There is no fallback to a shared account, because a quiet fallback would make the whole property unprovable.

What the reference protects against

Because the reference resolves only at the MCP, only against a token the trusted UI staged, and only inside its window, an attacker cannot hand the MCP a reference of their choosing and have it act as someone else. Redeeming it never returns the underlying token to the caller either. The MCP uses the staged token internally for the exchange and returns only the tool result.

07 Token exchange and RAR: making authority specific

Once the MCP has the real user token, it does not use it to talk to the database. It uses it to ask IBM Verify for a much smaller token. That is the RFC 8693 token exchange, and the reason it matters is the part most integrations skip: the exchange request carries RFC 9396 authorization_details describing exactly what the resulting token is for.

// RFC 9396 authorization_details sent with the RFC 8693 exchange (illustrative)
{
  "type": "refund",
  "actions": ["refund:create"],
  "resource": "retail/orders/84721",
  "amount": {
    "currency": "USD",
    "value": "42.50"
  }
}
    rfc 9396 · authorization_details

The exact schema is implementation-specific. Ours names the action, the receipt, and the instructed amount in the shape the Vault plugin expects. The security principle is not implementation-specific at all: authorization is bound to one action, one resource, and one amount. A token minted for this refund is not a token for the next refund, or for a larger one.

IBM Verify evaluates the request against policy and returns an on-behalf-of token. Here is a sanitized view of the claims that matter.

// On-behalf-of token claims returned by IBM Verify (sanitized, illustrative)
{
  "sub": "[email protected]",
  "act": {
    "sub": "agent:bigblue-returns"
  },
  "authorization_details": [
    {
      "type": "refund",
      "actions": ["refund:create"],
      "resource": "retail/orders/84721",
      "amount": { "currency": "USD", "value": "42.50" }
    }
  ]
}
    rfc 8693 · on-behalf-of token

Three claims carry the whole story. sub is the user on whose behalf the request is happening. act is the agent, the workload that actually performed the action. And authorization_details says exactly what that actor may do for that user. When I first decoded a live one of these from the running demo and saw my own identity in sub, the agent in act, and a single refund with a single amount in the details, the design stopped being a diagram.

The act claim deserves a moment. It is how a downstream system can tell "this user did this" apart from "this agent did this for this user." Both are true here, and the token says so. That distinction is what lets an auditor answer the question every agent deployment eventually gets asked: who was acting, and on whose authority?

08 Vault: issuing access only after authorization is specific enough

The on-behalf-of token is still not a database credential. What it carries is proof that a specific user, through a specific agent, is authorized for a specific action. The last step turns that proof into access, and it is the step that makes standing credentials unnecessary.

The MCP presents the token to HashiCorp Vault's verify-rar plugin. The plugin validates the authorization details against the role's constraints, and only then does Vault mint a Postgres role scoped to the retail schema, with a five-minute lease. In pseudocode, the shape of the credential path is this.

# Vault mints a role for this one call,
# then the MCP guarantees revocation (pseudocode)
cred = vault.read("${VAULT_ADDR}/v1/verify-rar/creds/${VAULT_ROLE}", obo_token)
# → username: v-refund-write-…   ttl: 300s
# → lease_id: verify-rar/creds/${VAULT_ROLE}/…

try:
    # role limited to the retail schema
    db = connect("${POSTGRES_HOST}", cred.username, cred.password)
    db.execute("UPDATE retail.receipts SET refunded_at = now()"
               "WHERE receipt_id = %s", receipt)
finally:
    vault.revoke(cred.lease_id)   # runs on success AND on error
    vault · verify-rar lease

Two details matter more than they look. The credential has a five-minute TTL, so even if revocation somehow failed, the exposure window is bounded. And revocation runs in a finally path, so the lease is released whether the refund succeeded or threw. A credential that is only revoked on the happy path is a credential that leaks on every unhappy one.

Put the three sections together and the property from section 03 holds without anyone having to trust the agent. Identity determines who may act. The exchange and the RAR determine what they may do. Vault releases a credential only when that request is specific enough to deserve one, and takes it back when the work is done.

BROWSER / UI HOSTED AGENT CONTEXTFORGE GOVERNED MCP ENTERPRISE MCP AUTHORIZATION VAULT CREDENTIAL RELEASE DATABASE 1Authenticate the user (OIDC + PKCE) 2Stage the user token with the MCP 3Generate the 2-minute sessionRef 4Send the A2A message (reference only) 5Agent selects the refund tool 6ContextForge routes the MCP call 7MCP resolves the reference (fail closed) 8IBM Verify: RFC 8693 exchange + RAR 9Verify returns OBO with act claim 10Vault validates the RAR 115-minute credential issued 12Postgres executes the refund 13Vault revokes the lease 14ARS pipeline retains the evidence
One refund across six trust boundaries. Only the reference crosses from the browser to the hosted agent, only the reference crosses through ContextForge, and every step that grants authority happens on the enterprise side and is recorded.

09 Agentic Runtime Security evidence and audit receipts

A security architecture you cannot see is one you have to take on faith, and I did not want anyone to take this on faith. So the demo makes the chain observable rather than invisible. Next to the chat there is an Agentic Runtime Security Pipeline panel, and a token-receipt view fed by the MCP's audit endpoint.

For every refund it shows the subject token and the on-behalf-of token, both decodable in place; the agent identity in the act claim; the authorization details for the exact action and amount; the Vault lease metadata; and whether the credential was revoked. And it shows one small chip that I ended up caring about more than anything else on the screen: a green status reading Real user · staged.

Why the green chip matters

It demonstrates that the refund did not run under a generic integration account or service identity. The system staged the real signed-in user context, then constrained the resulting authority to a specific action, amount, agent actor, and five-minute credential window.

The audit receipt behind that chip looks like this, sanitized and illustrative.

// One audit receipt from the ARS pipeline (sanitized, illustrative)
{
  "traceId": "refund-transaction-id",
  "user": "[email protected]",
  "agent": "bigblue-returns",
  "sessionRefStatus": "resolved",
  "tool": "issue_refund",
  "refund": { "orderId": "84721", "amount": "42.50", "currency": "USD" },
  "tokenExchange": "approved",
  "vaultLeaseTtlSeconds": 300,
  "credentialRevoked": true
}
    ars · audit receipt

Every claim in section 03 has a field here. The real user is user, and its source is the sub of the exchanged token. The acting agent is agent, from act. The specific action and amount are refund, from the authorization details. The policy decision is tokenExchange. The bounded credential is vaultLeaseTtlSeconds, and credentialRevoked records that the finally path did its job. If any of those were missing, the property would be a claim instead of a fact.

Demonstrated versus interpreted

What the running system demonstrates: a live refund whose on-behalf-of token carried a real user as sub, the agent as act, a single refund action and amount as authorization details, followed by a five-minute Vault credential and a recorded revocation. What is architectural interpretation: that the same seam and the same chain generalize to other systems of record. Section 11 makes that argument; the sections before it show the evidence.

10 What runs where

For readers who want to place each piece, this is the deployment shape. Operational values are placeholders.

ComponentLocationImplementation notes
ICA agent bigblue-returnsIBM Consulting Advantage Agent StudioStrands orchestration; defined in refund-ica/ica/bigblue-returns-agent.yaml; imported through the Agent Studio console; invoked externally through A2A
ContextForge gateway bigblue-returns-arsICA-hostedConfigured with bearer authentication; composes the MCP tools into the application virtual server; routes calls to the enterprise MCP
MCP serverEnterprise application hostrefund-ica-mcp systemd service; port 3017; loopback-bound; TypeScript on tsx; deployed by rsync and a service restart
Branded UIEnterprise application hostrefund-ica-ui systemd service; port 5181; SvelteKit adapter-node; production build deployed by rsync
IBM VerifyIBM Verify tenantOIDC authentication, token exchange, identity context, and authorization decisions
Vault and PostgresVault environmentverify-rar role refund-write; five-minute credentials for the retail schema

On exposure: the MCP publishes only the surfaces the integration needs, /mcp for ContextForge and /.well-known for discovery. The staging endpoint, the REST tool path, and the audit endpoint (POST /session/stage, /tool, /me/audit) are restricted enterprise endpoints used by the application and the local runtime. The browser session stages its token over that private path, which is why the token never has to appear on the public route the agent's calls take.

11 Why this integration pattern scales beyond refund agents

Nothing in the chain is specific to refunds except the vocabulary in the authorization details and the Postgres role Vault mints. Swap those and the same architecture governs a different action against a different system.

That is the practical value of the ContextForge seam. A hosted agent platform can participate in enterprise-grade identity and authorization architecture without becoming a trusted holder of credentials for every system it touches. The platform provides the governed connection point. The enterprise provides the policy service that turns an agent request into an identity-bound, action-specific decision, and the credential broker that turns that decision into access with an expiry. Other governed MCP services, internal APIs, systems of record, and security decision points can sit behind the same seam with the same division of labor.

It also changes what you have to trust. You do not have to trust that the agent will only ever do the right thing, because the agent cannot do more than the exchanged token allows, and the token cannot outlive the request. You have to trust IBM Verify to decide, Vault to release and revoke, and the MCP to fail closed. Those are systems built to be trusted with exactly that, and they leave evidence when they do it.

12 The agent can decide, but identity and policy define what it can actually do

The most important property of Big Blue Returns is not that it can process a refund. It is that the hosted agent never needs to become a privileged database client to do it.

ContextForge provides the governed connection. IBM Verify supplies identity and delegated authority. RFC 8693 and RFC 9396 make that authority specific. Vault turns it into a credential that expires before it becomes standing access.

The agent can make the decision. The architecture keeps the authority.

The live build is at ${DEMO_URL}. Sign in, refund a receipt, and open the pipeline panel. The green chip is waiting.

Sources