Human-in-the-loop approval platform

Out-of-band approvals via CIBA with budgets, timeouts, and a full decision log.

🛠 Advanced

Goal

Build a reusable HITL approval service that any agent can plug into: CIBA + WebAuthn step-up + capability-token issuance + user dashboard. Demonstrate that a fully-compromised agent can still only do user-approved actions.

Stack

  • IdP: Keycloak with CIBA enabled (oauth2.device.authorization.grant.enabled + CIBA settings).
  • Approval service: FastAPI + Postgres + Redis.
  • WebAuthn: webauthn Python lib + @simplewebauthn/browser for the front-end.
  • Front-end: React or SvelteKit dashboard ("My agents", "Pending approvals", "History").
  • Demo agent: existing helpdesk agent calling refund_order(order_id, amount).

Architecture

Agent → Approval service: POST /approvals { action, args, binding_message }
                          ↓ OPA policy decision (lesson 6.1)
                          ↓ Trigger CIBA at IdP OR push in-app notification
                          ↓ Persist approval record
User app   → Approval service: GET /approvals?status=pending
User taps Approve → WebAuthn ceremony → POST /approvals/{id}/approve {assertion}
Approval service → IdP /token (CIBA) → receive token with RAR
                  → mint capability token bound to agent's DPoP key
                  → return to agent
Agent       → payments-api with capability token + DPoP
payments-api enforces cap vs args + writes audit

Build plan (7 days)

  1. Configure Keycloak CIBA + mobile-push (or simulate with console pushes).
  2. Build approval-service Postgres schema (approvals, decisions, webauthn_credentials).
  3. Implement /approvals + /approvals/{id}/approve|reject + RAR construction.
  4. WebAuthn registration + assertion flows (passkey).
  5. Front-end: "Pending approvals" + "Connected agents" + "History" + "Disconnect agent".
  6. Capability-token minting service (extending lesson 6.1 work).
  7. Wire demo agent to request a refund; observe the full flow.
  8. Add expiry + cool-down + dual-approval option.
  9. Add dashboards: approval volume, rejection rate, time-to-decide, agent ranking.

Acceptance criteria

  • Approving via WebAuthn yields a capability token bound to that transaction.
  • A captured approval can't be reused (jti rejection).
  • Rejecting blocks the agent + logs the decision.
  • Expiry kills stale approvals.
  • Front-end shows full history per agent; user can revoke.
  • Dual approval works: two distinct users required for delete_tenant.

Stretch

  • Push via Twilio Verify + Auth0 Guardian as fallback channels.
  • Adaptive risk score driving when step-up is required.
  • Slack approvals for ops-style agents.
  • "Approval-fatigue" guardrail: warn the user when approvals exceed N/hr.
  • Integrate with the audit pipeline from project 1.

Interview talking points

  • Why per-action approval beats per-session.
  • Phishing-resistance of WebAuthn vs SMS.
  • CIBA binding_message design.
  • Approval-fatigue and how to combat it.
  • Dual approval failure modes (same-person-two-tabs).