CIBA: Human-in-the-Loop Authorization
Client Initiated Backchannel Authentication (CIBA) lets an AI agent pause and request explicit human approval before taking a sensitive action.
The Problem
Autonomous agents are powerful but risky. When an agent can:
- Send an email to a customer
- Execute a payment
- Delete records
- Post publicly on behalf of a user
...you need a way for a human to approve or deny the action before it happens.
How CIBA Works
Agent Bulwark User (mobile/email)
| | |
|-- POST /ciba/authorize→| |
| |-- notify user ---------->|
|<- authReqId ----------| |
| | |
| (polling loop) | (user reviews) |
|-- GET /ciba/token --->| |
|<- { status: pending } | |
| | |
| |<-- POST /ciba/approve ---|
| | |
|-- GET /ciba/token --->| |
|<- { status: approved, | |
| accessToken: ... }| |
| | |
| (execute action) | |
Implementation
1. Request approval
const { authReqId, expiresIn } = await bulwark.ciba.authorize({
userId: "usr_01j...",
scope: "approve:payment",
bindingMessage: "Approve payment of $500 to Acme Corp?",
requestedExpiry: 300, // 5 minutes
context: {
amount: 500,
recipient: "Acme Corp",
currency: "USD",
},
});
2. Poll for result
let approval;
while (true) {
approval = await bulwark.ciba.poll(authReqId);
if (approval.status === "approved") break;
if (approval.status === "denied") throw new Error("User denied");
if (approval.status === "expired") throw new Error("Approval expired");
await new Promise((r) => setTimeout(r, 5000)); // poll every 5s
}
// Proceed with the approved action
await executePayment(approval.accessToken);
3. User approves (dashboard/mobile)
The Bulwark dashboard and mobile app surface pending CIBA requests to users. Your app can also build a custom approval UI using the CIBA endpoints.
// In your approval UI
await bulwark.ciba.approve(authReqId);
// or
await bulwark.ciba.deny(authReqId);
LangChain Integration
import { CIBAApprovalTool } from "@bulwark/langchain";
const sendPaymentTool = new CIBAApprovalTool({
toolkit,
name: "send_payment",
description: "Send a payment (requires human approval)",
approvalScope: "approve:payment",
buildMessage: ({ amount, recipient }) =>
`Approve payment of $${amount} to ${recipient}?`,
onApproved: async ({ amount, recipient }) => {
// executed only after human approves
return await paymentGateway.send({ amount, recipient });
},
});
Vercel AI SDK Integration
import { createCIBATool } from "@bulwark/vercel-ai";
const tool = createCIBATool({
session,
userId: "usr_01j...",
name: "delete_record",
description: "Delete a customer record — requires human approval",
approvalScope: "approve:delete",
parameters: z.object({ customerId: z.string() }),
buildMessage: ({ customerId }) =>
`Permanently delete customer ${customerId}? This cannot be undone.`,
onApproved: ({ customerId }) => db.customers.delete(customerId),
});
When to Require CIBA
Use CIBA for any agent action that:
- Is irreversible (delete, publish, send)
- Involves money or sensitive data
- Acts on behalf of a user in the outside world
- Has significant business impact
Configure trust-level-based CIBA requirements in the Bulwark dashboard — critical trust agents always require CIBA for defined action categories.