developers

Secure “Human in the Loop” Interactions for AI Agents

AI agents are making decisions without you? Explore the challenges of AI autonomy and discover why human oversight is crucial for responsible AI. Learn how asynchronous authorization and CIBA can help you keep humans in the loop for critical AI actions.

Imagine a world where AI agents handle complex tasks on your behalf – managing your finances, optimizing energy consumption in your home, or even coordinating logistics for a global supply chain. The potential benefits are enormous: increased efficiency, data-driven decisions, and automation of tedious processes. But what happens when these agents need to perform critical actions? Should an AI agent automatically buy a large amount of stock, transfer funds, or update sensitive records without any human oversight?

Most of us would probably prefer to have a say in those decisions. We want AI to augment our abilities, not replace our judgment, especially when high-stakes actions are involved. The challenge is: how do we ensure human confirmation before an AI agent executes a sensitive action without disrupting the agent's workflow or creating a clunky user experience? Traditional, synchronous authorization methods can be a real pain in this scenario.

What Are AI Agents?

Let's take a step back and define what we mean by "AI agent." Essentially, an AI agent is an autonomous entity that can perceive its environment, make decisions based on that perception, and take actions to achieve specific goals. Think of it as a software robot with a brain (powered by AI, of course).

We're already seeing AI agents pop up in various domains:

  • Finance: Automated trading bots and fraud detection systems.
  • Healthcare: Diagnostic tools, personalized treatment recommendations.
  • IoT: Smart home controllers, automated industrial processes.
  • Cybersecurity: Threat detection and response systems.

The future potential of AI agents is vast. They could revolutionize industries, create personalized experiences, and unlock entirely new business models. However, this potential comes with responsibilities. We need to address concerns about job displacement, ethical considerations, and security risks. That's why responsible AI development and human oversight are so crucial.

Asynchronous User Confirmation

So, how do we keep humans in the loop without slowing down our AI agents? The answer lies in asynchronous user authorization.

Asynchronous user authorization decouples the authorization request from the actual action. Instead of requiring immediate approval, the AI agent can request authorization and then continue its work while waiting for a response. This offers several key benefits:

  • Non-Blocking: The AI agent doesn't have to wait idly for human confirmation. It can continue processing other tasks or monitoring the environment.
  • Improved User Experience: Users can authorize actions at their convenience without being forced to respond immediately. Imagine getting a notification on your phone asking you to approve a stock trade, and you can do it with a single tap whenever you have a moment.
  • Enhanced Security: Asynchronous authorization provides an extra layer of protection against unauthorized actions. Even if an AI agent is compromised, it can't execute sensitive actions without explicit human approval.

Here's a diagram that illustrates the flow:

Agent Flow

Let's break down what's happening in the diagram:

  1. The AI Agent needs to perform a sensitive action (e.g., "Buy Stock").
  2. It sends an authorization request to the Authorization Server, specifying the action it wants to perform.
  3. The Authorization Server notifies the user (e.g., via push notification, email, or SMS) and prompts them to approve or deny the request.
  4. The user reviews the request and makes a decision.
  5. The Authorization Server informs the AI Agent of the user's decision.
  6. The AI Agent executes the action only if authorization is granted.

Enter CIBA: A Standard for Asynchronous Authorization

Now, you might be thinking, "This sounds great, but how do I actually implement this?" That's where CIBA (Client Initiated Backchannel Authentication) comes in.

CIBA is a standardized protocol for asynchronous authorization. It provides a secure and interoperable way for AI agents to request authorization from users without relying on traditional browser-based redirects.

Think of CIBA as a common language that AI agents and authorization servers can use to communicate securely and efficiently. It offers several key features:

  • Backchannel Communication: The AI agent and the authorization server communicate directly using secure API calls. This eliminates the need for browser redirects, which can be clunky and insecure.
  • User Device Notification: The authorization server can notify the user on any device (e.g., smartphone, tablet, desktop) using push notifications, SMS, or other channels.
  • Standardized Protocol: CIBA is a well-defined standard that ensures interoperability between different AI agents and authorization servers. This means you can use a CIBA-compliant AI agent with any CIBA-compliant authorization server without having to worry about compatibility issues.

For more information, please refer to the CIBA flow and how to get started with Auth0 and CIBA documentation.

AI Agent Example with CIBA in JavaScript

Let's look at a simple JavaScript example to see how an AI agent can use CIBA to request authorization for an action. This is a simplified example, but it illustrates the core concepts.

Note: This example assumes you have a CIBA-compliant authorization server. For a streamlined experience, consider using Auth for GenAI, which provides the platforms and SDKs you need to build AI agents that implement asynchronous user confirmation.

import { Auth0AI, Auth0State } from "@auth0/ai-langchain";

// Instantiate Auth0AI
const auth0AI = new Auth0AI();

/**
 * Configures the CIBA flow with Auth0 AI.
 */
const ciba = auth0AI.withCIBA({
  audience: process.env["AUDIENCE"],
  config: {
    onResumeInvoke: "conditional-tool-call-example",
    scheduler: async (input) => {
      // Custom scheduler
      await SchedulerClient().schedule(input.cibaGraphId, { input });
    },
  },
});

// Define the state annotation
const StateAnnotation = Annotation.Root({
  ...MessagesAnnotation.spec,
  ...Auth0State.spec,
  data: Annotation<ToolCallExampleType>(),
});

// Define a new State Grapth with CIBA protection
const stateGraph = ciba.registerNodes(
  new StateGraph(StateAnnotation)
    .addNode("checkCondition", checkCondition)
    .addNode("notifyUser", notifyUser)
    .addNode("stopScheduler", stopScheduler)
    .addNode(
      "tools",
      new ToolNode([
        ciba.protectTool(tradeTool, {
          onApproveGoTo: "tools",
          onRejectGoTo: "stopScheduler",
          scope: "example:trade",
          binding_message: async (_) => {
            return `Do you want to call example? ${_.toolProperty}`;
          },
        }),
      ])
    )
    .addEdge(START, "checkCondition")
    .addEdge("tools", "stopScheduler")
    .addEdge("stopScheduler", "notifyUser")
    .addConditionalEdges("checkCondition", ciba.withAuth(shouldContinue))
);

const checkpointer = new MemorySaver();
const store = new InMemoryStore();

// Compile the graph
export const graph = stateGraph.compile({
  checkpointer,
  store,
});

Let's walk through the code:

  1. First, we instantiate
    Auth0AI
    and the configurations for the CIBA flow.
  2. Then we define the state annotations for the graph with all the annotations required for the Graph, plus the CIBA flow-specific annotations.
  3. To build the Graph we must wrap the StateGraph with the
    ciba.registerNodes
    , this will enable the CIBA functionality on the given graph.
  4. In the graph we define multiple nodes, each node would represent a step or tool in the graph, but we also add a special
    ToolNode
    that we protect with CIBA through the
    protectTool
    method.
    • This method expects the nodes in the graph to jump to when the request is approved by the user, or rejected by the user, as well as the scope required for the approval and the binding message.

In further blog posts, we will share full code samples and step-by-step guidance on how to implement Async Confirmation for users in complex workflow scenarios using LangGraph and other frameworks.

Conclusion

As AI agents become more prevalent, it's crucial to ensure that they operate responsibly and ethically. Asynchronous user authorization, powered by standards like CIBA, provides a powerful mechanism for keeping humans in the loop without sacrificing the efficiency and autonomy of AI systems.

By embracing asynchronous authorization, we can build AI systems that are both powerful and responsible, ensuring that humans remain in control where it matters most. Let's work together to create a future where AI augments our abilities and empowers us to make better decisions.

And be sure to check out Auth for GenAI to learn more about authentication and authorization for AI applications, including features like async user confirmation, fine-grained access control, async authentication, and calling APIs on the user’s behalf.

The future of AI is in our hands; let’s build it responsibly.

Thanks for reading!