Skip to main content

Asynchronous Authorization

Auth for GenAI enables AI agents to asynchronously authorize users using the Client-Initiated Backchannel Authentication Flow. AI agents can work in the background, only notifying the user when needed for critical actions.

When you add secure human-in-the-loop approvals to your AI agent workflows, you can use Auth0 to request the user's permission to complete an authorization request. The AI agent can render rich authorization data in the consent prompt so the user knows exactly what they’re authorizing.

By the end of this quickstart, you should have an AI agent integrated with the Auth0 AI SDK that can request to buy stock on the user's behalf.

note

We value your feedback! To ask questions, report issues, or request new frameworks and providers, connect with us on GitHub.

Pick Your Tech Stack

Coming soon!

Prerequisites

Before getting started, make sure you have completed the following steps:

unchecked

Create an Auth0 Account and a Dev Tenant

To continue with this quickstart, you need an Auth0 account and a Developer Tenant.

unchecked

Create Application

Create and configure a Machine to Machine Application to use with this quickstart.
To learn more about Auth0 applications, read ApplicationsArrow icon

unchecked

Enable Guardian Push

Enable Mutli-factor authentication (MFA) with Guardian Push Notifications for your Auth0 tenant.
To learn more about MFA with Guardian, read the Auth0 Guardian documentationArrow icon

Enroll your user to use Auth0 Guardian

Enroll your user to use Auth0 Guardian

To initiate a CIBA push request, the authorizing user must be enrolled in MFA using push notifications. To verify if the authorizing user is enrolled for MFA push notifications in the Auth0 DashboardArrow icon, navigate to User Management > Users and click on the user.

Under Multi-Factor Authentication, Auth0 lists the factors the user is enrolled in:

User Enrolled in Auth0 Guardian

If the user is not enrolled, you can send an enrollment request by email:

Enable Guardian Push Screenshot
OpenAI Platform

OpenAI Platform

Install dependencies

Install the following dependencies:

  • ai: Core Vercel AI SDK module that interacts with various AI model providers.
  • zod: TypeScript-first schema validation library.
  • dotenv: A module that loads environment variables from a .env file.
  • auth0: Auth0 Node.js library.
  • @ai-sdk/openai: OpenAI provider for the Vercel AI SDK.
  • @auth0/ai-vercel: Auth0 AI SDK for Vercel AI built for GenAI applications powered by the Vercel AI SDK.
Create a new Node.js project
ctrl+C
npm init -y
npm i auth0 @auth0/ai-vercel zod ai @ai-sdk/openai dotenv

Add the below to package.json:

package.json
ctrl+C
"main": "index.js",
"x-type": "module",
"scripts": {
"start": "npx tsx src/index.ts"
},

Integrate Auth0 AI SDK

Integrate the Auth0 AI SDK into your application to secure your async AI agent workflow.

1. Create your environment file

In the root directory of your project, create the .env file and add the following variables. If you created an application with this quickstart, Auth0 automatically populates your environment variables for you:

note

Your application’s client secret is masked for you. To get the client secret value, click the copy button on the code sample.

.env
ctrl+C
# .env
AUTH0_DOMAIN='<your-auth0-domain>'
AUTH0_CLIENT_ID='<your-auth0-application-client-id>'
AUTH0_CLIENT_SECRET='<your-auth0-application-client-secret>'

# You can use any provider of your choice supported by Vercel AI
OPENAI_API_KEY="YOUR_API_KEY"

# API
STOCK_API_URL=<your-stock-api-url>
AUDIENCE=sample-stock-api

2. Require async authorization for your tool

To require asynchronous authorization for your tool:

  1. Define a context under which the AI agent will run.
  2. Wrap the tool with the Async authorizer, withAsyncUserConfirmation().

First, we create a file at src/types/ai-context.ts. In the file, we define a context under which the AI agent will run. In our example, we need to know the user ID that will approve the request.

./src/types/ai-context.ts
ctrl+C
export type Context = {
userId: string;
};

Then, in src/lib/tools/buy.ts, we wrap the tool with the Async authorizer to intercept the tool call to initiate a CIBA request:

  • The CIBA request includes the user ID that will approve the request.
  • The Auth0 Guardian app sends the user a mobile push notification. The AI agent polls the /token endpoint for a user response.
  • The mobile application retrieves the binding_message containing the consent details, in this case, the quantity of stock to purchase for a stock ticker.
  • The user responds to the request:
    • If the request is approved, the tool execution will continue.
    • If the request is rejected, the tool execution will not continue.

CIBA sequence diagram

In our example, we wrap a tool that buys shares for a given stock picker on the user’s behalf. When the user approves the transaction, the Auth0 AI SDK retrieves an access token to call the stock’s API. Upon completing the CIBA flow, the AI agent responds with a message confirming the purchase.

The Auth0 AI SDK returns an error response if the user denies the transaction.

./src/lib/tools/buy.ts
ctrl+C
import "dotenv/config";

import { tool } from "ai";
import { z } from "zod";

import { Auth0AI, getCIBACredentials } from "@auth0/ai-vercel";
import { AccessDeniedInterrupt } from "@auth0/ai/interrupts";
import { Context } from "../../types/ai-context";

const auth0AI = new Auth0AI();

export const buy = (context: Context) => {
const withAsyncAuthorization = auth0AI.withAsyncUserConfirmation({
userID: context.userID,
bindingMessage: async ({ ticker, qty }) =>
`Do you want to buy ${qty} shares of ${ticker}`,
scopes: ["openid", "stock:trade"],
audience: process.env["AUDIENCE"]!,
/**
* When this flag is set to `block`, the execution of the tool awaits
* until the user approves or rejects the request.
*
* Given the asynchronous nature of the CIBA flow, this mode
* is only useful during development.
*
* In practice, the process that is awaiting the user confirmation
* could crash or timeout before the user approves the request.
*/
onAuthorizationRequest: "block",
onUnauthorized: async (e: Error) => {
if (e instanceof AccessDeniedInterrupt) {
return "The user has deny the request";
}
return e.message;
},
});

return withAsyncAuthorization(
tool({
description: "Use this function to buy stock",
parameters: z.object({
ticker: z.string(),
qty: z.number(),
}),
execute: async ({ ticker, qty }) => {
const headers = {
"Content-Type": "application/json",
};
const body = {
ticker: ticker,
qty: qty,
};
const credentials = getCIBACredentials();
const accessToken = credentials?.accessToken?.value;

if (accessToken) {
headers["Authorization"] = "Bearer " + accessToken;
}

console.log("Executing request to buy stock");

const response = await fetch(process.env["STOCK_API_URL"]!, {
method: "POST",
headers: headers,
body: JSON.stringify(body),
});

return response.statusText;
},
})
);
};

3. Integrate the tool into an AI agent

Create a file at src/lib/llm.ts and add tool calling to the Vercel AI agent using the generateText() or streamText() functions.

./src/lib/llm.ts
ctrl+C
import { openai } from "@ai-sdk/openai";
import { CoreMessage, generateText } from "ai";
import { Context } from "../types/ai-context";
import { buy } from "./tools/buy.ts";

export async function askLLM(messages: CoreMessage[], context: Context) {
return generateText({
model: openai("gpt-4o-mini"),
messages,
maxSteps: 2,
tools: {
buy: buy(context),
},
});
}

You should now have a CLI app that allows you to interact with the AI agent.

Test the application

To test the CLI app, set a prompt, such as "Buy 3 stocks of Google," and pass it the user ID of the user approving or declining the transaction. You can get the user ID from the Auth0 Dashboard. Navigate to User Management > Users and click on a user. The ID should look like auth0|123456789.

./src/index.ts
ctrl+C
import { setAIContext } from "@auth0/ai-vercel";
import { askLLM } from "./lib/llm";
import crypto from "node:crypto";

async function main() {
const threadID = crypto.randomUUID();
setAIContext({ threadID });
console.log(
"Check your mobile device for Auth0 Guardian notification and approve the request"
);
const { response, text } = await askLLM(
[{ role: "user", content: "Buy 3 stocks of Google" }],
// Provide the Auth0 User Id of the approver. Something like 'auth0|123456789'.
{ userID: "<authenticated-user-id>" }
);

console.log(text);
}

main().catch(console.error);

Now, run npm start and look for a push notification from the Auth0 Guardian App on your mobile device. Once you approve the notification, you should see the tool being executed on your console.

Explore the example app on GitHub.

// TODO: Python doc goes here

Next steps

You have successfully added an authorization step to protect tool calling in asynchronous AI agents. For next steps: