From 048fe51951d146c7db28f644d0f92940d319c295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Wo=C5=BAniewski?= Date: Wed, 29 Oct 2025 15:13:22 +0100 Subject: [PATCH 1/9] AIDX-313 Add update for Cloudflare example docs --- .../cloudflare-agents-js/call-your-api.mdx | 187 +++++++++++-- .../how-tos/github/cloudflare-agents.mdx | 43 +-- .../google-calendar/cloudflare-agents.mdx | 250 +++++++++++------- .../how-tos/slack/cloudflare-agents.mdx | 192 +++++++++----- 4 files changed, 457 insertions(+), 215 deletions(-) diff --git a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx index 988d26329..4e57bea04 100644 --- a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx +++ b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx @@ -42,6 +42,27 @@ npm install hono \ In the root directory of your project, copy the `.dev.vars.example` into `.dev.vars` file and configure the Auth0 and OpenAI variables. +```bash .dev.vars wrap lines +# ... +# You can use any provider of your choice supported by Vercel AI +OPENAI_API_KEY="OPENAI API KEY" + +SESSION_STORE=cloudflare-kv +SESSION_STORE_NAMESPACE=Session + + +#auth0 +AUTH0_DOMAIN="YOUR-ACCOUNT.us.auth0.com" +AUTH0_CLIENT_ID="YOUR CLIENT ID" +AUTH0_CLIENT_SECRET="YOUR CLIENT SECRET" +AUTH0_SESSION_ENCRYPTION_KEY="RANDOM 32 CHARS" +AUTH0_AUDIENCE="YOUR AUDIENCE" + +BASE_URL="http://localhost:3000" +``` + +If you use another provider for your LLM, adjust the variable name in `.dev.vars` accordingly. + ### Define a tool to call your API In this step, you'll create a Vercel AI tool to make the first-party API call to the Auth0 API. You will do the same for third-party APIs. @@ -53,41 +74,159 @@ Since the Agent defined in the class Chat in `src/agent/chat.ts` uses the **Auth The tool we are defining here uses the same access token to call Auth0's [`/userinfo`](https://auth0.com/docs/api/authentication/user-profile/get-user-info) endpoint. ```tsx src/agent/tools.ts wrap lines -const getUserInfoTool = tool({ - description: "Get information about the current logged in user.", - parameters: z.object({}), - execute: async () => { - const { agent } = getCurrentAgent(); - const tokenSet = agent?.getCredentials(); - if (!tokenSet) { - return "There is no user logged in."; +/** + * Tool definitions for the AI chat agent + * Tools can either require human confirmation or execute automatically + */ +import { tool } from "ai"; +import { z } from "zod"; + +import { getCurrentAgent } from "agents"; +import { unstable_scheduleSchema } from "agents/schedule"; +import { format, toZonedTime } from "date-fns-tz"; +import { buyStock } from "./auth0-ai-sample-tools/buy-stock"; +import { checkUsersCalendar } from "./auth0-ai-sample-tools/check-user-calendar"; + +/** + * Weather information tool that requires human confirmation + * When invoked, this will present a confirmation dialog to the user + * The actual implementation is in the executions object below + */ +const getWeatherInformation = tool({ + description: "show the weather in a given city to the user", + inputSchema: z.object({ city: z.string() }), +}); + +/** + * Local time tool that executes automatically + * Since it includes an execute function, it will run without user confirmation + * This is suitable for low-risk operations that don't need oversight + */ +const getLocalTime = tool({ + description: "get the local time for a specified location", + inputSchema: z.object({ + timeZone: z.string().describe("IANA time zone name"), + }), + execute: async ({ timeZone: location }) => { + const now = new Date(); + const zonedDate = toZonedTime(now, location); + const output = format(zonedDate, "yyyy-MM-dd HH:mm:ssXXX", { + timeZone: location, + }); + return output; + }, +}); + +const scheduleTask = tool({ + description: "A tool to schedule a task to be executed at a later time", + inputSchema: unstable_scheduleSchema, + execute: async ({ when, description }) => { + const { agent } = getCurrentAgent(); + + function throwError(msg: string): string { + throw new Error(msg); + } + if (when.type === "no-schedule") { + return "Not a valid schedule input"; + } + const input = + when.type === "scheduled" + ? when.date // scheduled + : when.type === "delayed" + ? when.delayInSeconds // delayed + : when.type === "cron" + ? when.cron // cron + : throwError("not a valid schedule input"); + try { + agent!.schedule(input!, "executeTask" as keyof typeof agent, description); + } catch (error) { + console.error("error scheduling task", error); + return `Error scheduling task: ${error}`; } + return `Task scheduled for type "${when.type}" : ${input}`; + }, +}); - const response = await fetch( - `https://${process.env.AUTH0_DOMAIN}/userinfo`, - { - headers: { - Authorization: `Bearer ${tokenSet.access_token}`, - }, - } - ); +/** + * Tool to list all scheduled tasks + * This executes automatically without requiring human confirmation + */ +const getScheduledTasks = tool({ + description: "List all tasks that have been scheduled", + inputSchema: z.object({}), + execute: async () => { + const { agent } = getCurrentAgent(); - if (response.ok) { - return { result: await response.json() }; + try { + const tasks = agent!.getSchedules(); + if (!tasks || tasks.length === 0) { + return "No scheduled tasks found."; + } + return tasks; + } catch (error) { + console.error("Error listing scheduled tasks", error); + return `Error listing scheduled tasks: ${error}`; } + }, +}); - return "I couldn't verify your identity"; +/** + * Tool to cancel a scheduled task by its ID + * This executes automatically without requiring human confirmation + */ +const cancelScheduledTask = tool({ + description: "Cancel a scheduled task using its ID", + inputSchema: z.object({ + taskId: z.string().describe("The ID of the task to cancel"), + }), + execute: async ({ taskId }) => { + const { agent } = getCurrentAgent(); + try { + await agent!.cancelSchedule(taskId); + return `Task ${taskId} has been successfully canceled.`; + } catch (error) { + console.error("Error canceling scheduled task", error); + return `Error canceling task ${taskId}: ${error}`; + } }, }); + +/** + * Export all available tools + * These will be provided to the AI model to describe available capabilities + */ +export const tools = { + getWeatherInformation, + getLocalTime, + scheduleTask, + getScheduledTasks, + cancelScheduledTask, + checkUsersCalendar, + buyStock, +}; + +/** + * Implementation of confirmation-required tools + * This object contains the actual logic for tools that need human approval + * Each function here corresponds to a tool above that doesn't have an execute function + */ +export const executions = { + getWeatherInformation: async ({ city }: { city: string }) => { + console.log(`Getting weather information for ${city}`); + return `The weather in ${city} is sunny`; + }, +}; ``` -Then in the `tools` export of the `src/agent/chat.ts` file, add the `getUserInfoTool` to the tools array: +Then in the `tools` export of the `src/agent/chat.ts` file, add the `tools` to the `allTools` array: ```tsx src/agent/chat.ts wrap lines -export const tools = { - // Your other tools... - getUserInfoTool, -}; +async onChatMessage() { + const allTools = { + ...tools, + ...(this.mcp?.getAITools?.() ?? {}), + }; + ... // The rest of the code ``` ### Test your application diff --git a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx index c5b6f3dcc..24619446a 100644 --- a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx @@ -19,30 +19,35 @@ npm install @auth0/ai-vercel @auth0/ai-cloudflare @auth0/ai Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required GitHub scopes. -```typescript ./src/lib/auth0-ai.ts wrap lines +```typescript ./src/agent/auth0-ai.ts wrap lines import { Auth0AI, setGlobalAIContext } from "@auth0/ai-vercel"; import { getCurrentAgent } from "agents"; -import type { Chat } from "./chat"; + +setGlobalAIContext(() => ({ threadID: getAgent().name })); + +const auth0AI = new Auth0AI({ + store: () => { + return (getAgent() as any).auth0AIStore; + }, +}); const getAgent = () => { - const { agent } = getCurrentAgent(); + const { agent } = getCurrentAgent(); if (!agent) { throw new Error("No agent found"); } return agent; }; -setGlobalAIContext(() => ({ threadID: getAgent().name })); - -const auth0AI = new Auth0AI(); +const refreshToken = async () => { + const credentials = (getAgent() as any).getCredentials(); + return credentials?.refresh_token; +}; export const withGitHub = auth0AI.withTokenVault({ + refreshToken, connection: "github", scopes: ["repo"], - refreshToken: async () => { - const credentials = getAgent().getCredentials(); - return credentials?.refresh_token; - }, }); ``` @@ -50,19 +55,19 @@ export const withGitHub = auth0AI.withTokenVault({ Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API. -```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={2-4,9,15,19-21,31-33} +```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={6-7,9,15,19-21,28-30} +import { tool } from "ai"; +import { z } from "zod/v3"; + import { Octokit, RequestError } from "octokit"; import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel"; import { TokenVaultError } from "@auth0/ai/interrupts"; -import { withGitHub } from "@/lib/auth0-ai"; -import { tool } from "ai"; -import { z } from "zod"; - +import { withGitHub } from "@/agent/auth0-ai"; export const listRepositories = withGitHub( tool({ description: "List respositories for the current user on GitHub", - parameters: z.object({}), + inputSchema: z.object({}), execute: async () => { // Get the access token from Auth0 AI const accessToken = getAccessTokenFromTokenVault(); @@ -72,17 +77,14 @@ export const listRepositories = withGitHub( const octokit = new Octokit({ auth: accessToken, }); - const { data } = await octokit.rest.repos.listForAuthenticatedUser(); return data.map((repo) => repo.name); } catch (error) { - console.log("Error", error); - if (error instanceof RequestError) { if (error.status === 401) { throw new TokenVaultError( - `Authorization required to access the Token Vault connection` + `Authorization required to access the Token Vault` ); } } @@ -92,6 +94,7 @@ export const listRepositories = withGitHub( }, }) ); + ``` ### 3. Handle authentication redirects diff --git a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx index de6e32c97..6e0665d3b 100644 --- a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx @@ -17,7 +17,7 @@ npm install @auth0/ai-vercel @auth0/ai-cloudflare @auth0/ai Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required Google Calendar scopes. -```typescript ./src/lib/auth0-ai.ts wrap lines +```typescript ./src/agent/auth0-ai.ts wrap lines import { Auth0AI, setGlobalAIContext } from "@auth0/ai-vercel"; import { getCurrentAgent } from "agents"; import type { Chat } from "./chat"; @@ -35,12 +35,12 @@ setGlobalAIContext(() => ({ threadID: getAgent().name })); const auth0AI = new Auth0AI(); export const withGoogleCalendar = auth0AI.withTokenVault({ - connection: "google-oauth2", - scopes: ["https://www.googleapis.com/auth/calendar.freebusy"], refreshToken: async () => { - const credentials = getAgent().getCredentials(); + const credentials = (getAgent() as any).getCredentials(); return credentials?.refresh_token; }, + connection: "google-oauth2", + scopes: ["https://www.googleapis.com/auth/calendar.freebusy"], }); ``` @@ -48,60 +48,57 @@ export const withGoogleCalendar = auth0AI.withTokenVault({ Wrap your tool using the Auth0 AI SDK to obtain an access token for the Google Calendar API. -```typescript ./src/agent/tools/checkUsersCalendar.ts wrap lines highlight={4-6,10,19,26-28.46-48} -import { addHours, formatISO } from "date-fns"; -import { GaxiosError } from "gaxios"; -import { google } from "googleapis"; +```typescript ./src/agent/auth0-ai-sample-tools/check-user-calendar.ts wrap lines highlight={1-2,6,8,17,37-39} import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel"; import { TokenVaultError } from "@auth0/ai/interrupts"; -import { withGoogleCalendar } from "@/lib/auth0-ai"; import { tool } from "ai"; +import { addHours } from "date-fns"; import { z } from "zod"; +import { withGoogleCalendar } from "../auth0-ai"; export const checkUsersCalendar = withGoogleCalendar( tool({ description: "Check user availability on a given date time on their calendar", - parameters: z.object({ + inputSchema: z.object({ date: z.coerce.date(), }), execute: async ({ date }) => { // Get the access token from Auth0 AI const accessToken = getAccessTokenFromTokenVault(); - - // Google SDK - try { - const calendar = google.calendar("v3"); - const auth = new google.auth.OAuth2(); - - auth.setCredentials({ - access_token: accessToken, - }); - - const response = await calendar.freebusy.query({ - auth, - requestBody: { - timeMin: formatISO(date), - timeMax: addHours(date, 1).toISOString(), - timeZone: "UTC", - items: [{ id: "primary" }], - }, - }); - - return { - available: response.data?.calendars?.primary?.busy?.length === 0, - }; - } catch (error) { - if (error instanceof GaxiosError) { - if (error.status === 401) { - throw new TokenVaultError( - `Authorization required to access the Token Vault connection` - ); - } + const url = "https://www.googleapis.com/calendar/v3/freeBusy"; + const body = JSON.stringify({ + timeMin: date, + timeMax: addHours(date, 1), + timeZone: "UTC", + items: [{ id: "primary" }], + }); + + const response = await fetch(url, { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body, + }); + + if (!response.ok) { + if (response.status === 401) { + throw new TokenVaultError( + "Authorization required to access the Federated Connection" + ); } - - throw error; + throw new Error( + `Invalid response from Google Calendar API: ${ + response.status + } - ${await response.text()}` + ); } + + const busyResp = await response.json(); + + return { available: busyResp.calendars.primary.busy.length === 0 }; }, }) ); @@ -117,78 +114,129 @@ Interrupts are a way for the system to pause execution and prompt the user to ta On the Chat agent class, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. -```typescript ./src/agent/chat.ts wrap lines highlight={1-2,52-54,63-66} -import { setAIContext } from "@auth0/ai-vercel"; -import { errorSerializer, withInterruptions } from "@auth0/ai-vercel/interrupts"; -// Other dependencies -import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; +```typescript ./src/agent/chat.ts wrap lines highlight={6-10,52,54-57,101} import { openai } from "@ai-sdk/openai"; +import { + AsyncUserConfirmationResumer, + CloudflareKVStore, +} from "@auth0/ai-cloudflare"; +import { + errorSerializer, + invokeTools, + withInterruptions, +} from "@auth0/ai-vercel/interrupts"; +import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; import { AIChatAgent } from "agents/ai-chat-agent"; +import { + convertToModelMessages, + createUIMessageStream, + createUIMessageStreamResponse, + generateId, + stepCountIs, + streamText, + type UIMessage, +} from "ai"; +import { executions, tools } from "./tools"; +import { processToolCalls } from "./utils"; + +const model = openai("gpt-4o-2024-11-20"); + +class BaseChat extends AIChatAgent {} + +const AuthedChat = AuthAgent(BaseChat); +const OwnedAuthedChat = OwnedAgent(AuthedChat); +const ResumableOwnedAuthedChat = AsyncUserConfirmationResumer(OwnedAuthedChat); + +export class Chat extends ResumableOwnedAuthedChat { + messages: UIMessage[] = []; + + declare mcp?: + | { + unstable_getAITools?: () => Record; + } + | undefined; -const SuperAgent = OwnedAgent(AuthAgent(AIChatAgent)); - -export class Chat extends SuperAgent { - async onChatMessage( - onFinish: StreamTextOnFinishCallback, - options?: { abortSignal?: AbortSignal } - ) { - // Collect all tools, including MCP tools + async onChatMessage() { const allTools = { ...tools, - ...this.mcp.unstable_getAITools(), + ...(this.mcp?.unstable_getAITools?.() ?? {}), }; - const claims = this.getClaims(); - // Create a streaming response that handles both text and tool outputs - const dataStreamResponse = createDataStreamResponse({ - execute: withInterruptions(async (dataStream) => { - // Process any pending tool calls from previous messages - // This handles human-in-the-loop confirmations for tools - const processedMessages = await processToolCalls({ - messages: this.messages, - dataStream, - tools: allTools, - executions, - }); - - // Stream the AI response using GPT-4 - const result = streamText({ - model, - system: `You are a helpful assistant that can do various tasks... - -${unstable_getSchedulePrompt({ date: new Date() })} + + const claims = this.getClaims?.(); + + const stream = createUIMessageStream({ + originalMessages: this.messages, + execute: withInterruptions( + async ({ writer }) => { + await invokeTools({ + messages: convertToModelMessages(this.messages), + tools: allTools, + }); + + const processed = await processToolCalls({ + messages: this.messages, + dataStream: writer, + tools: allTools, + executions, + }); + + const result = streamText({ + model, + stopWhen: stepCountIs(10), + messages: convertToModelMessages(processed), + system: `You are a helpful assistant that can do various tasks... If the user asks to schedule a task, use the schedule tool to schedule the task. -The name of the user is ${claims?.name ?? "unknown"}. -`, - messages: processedMessages, - tools: allTools, - onFinish: async (args) => { - onFinish( - args as Parameters>[0] - ); - }, - onError: (error) => { - if (!Auth0Interrupt.isInterrupt(error)) { - return; - } - console.error("Error while streaming:", error); - }, - maxSteps: 10, - }); - - // Merge the AI response stream with tool execution outputs - result.mergeIntoDataStream(dataStream); - }), - onError: errorSerializer((err) => { - console.log(err); - return "Oops, an error occured!"; - }), +The name of the user is ${claims?.name ?? "unknown"}.`, + tools: allTools, + onStepFinish: (output) => { + if (output.finishReason === "tool-calls") { + const last = output.content[output.content.length - 1]; + if (last?.type === "tool-error") { + const { toolName, toolCallId, error, input } = last; + const serializableError = { + cause: error, + toolCallId, + toolName, + toolArgs: input, + }; + throw serializableError; + } + } + }, + }); + + writer.merge( + result.toUIMessageStream({ + sendReasoning: true, + }) + ); + }, + { messages: this.messages, tools: allTools } + ), + onError: errorSerializer(), }); - return dataStreamResponse; + return createUIMessageStreamResponse({ stream }); + } + + async executeTask(description: string) { + await this.saveMessages([ + ...this.messages, + { + id: generateId(), + role: "user", + parts: [{ type: "text", text: `Running scheduled task: ${description}` }], + }, + ]); + } + + get auth0AIStore() { + return new CloudflareKVStore({ kv: (this as any).env.Session }); } } + ``` #### Client Side diff --git a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx index bb11619b7..057414669 100644 --- a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx @@ -17,7 +17,7 @@ npm install @auth0/ai-vercel @auth0/ai-cloudflare @auth0/ai Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required GitHub scopes. -```typescript ./src/lib/auth0-ai.ts wrap lines +```typescript ./src/agent/auth0-ai.ts wrap lines import { Auth0AI, setGlobalAIContext } from "@auth0/ai-vercel"; import { getCurrentAgent } from "agents"; import type { Chat } from "./chat"; @@ -35,12 +35,13 @@ setGlobalAIContext(() => ({ threadID: getAgent().name })); const auth0AI = new Auth0AI(); export const withSlack = auth0AI.withTokenVault({ - connection: "sign-in-with-slack", - scopes: ["channels:read", "groups:read"], refreshToken: async () => { - const credentials = getAgent().getCredentials(); - return credentials?.refresh_token; + const session = await auth0AI.getSession(); + const refreshToken = session?.tokenSet.refreshToken as string; + return refreshToken; }, + connection: "sign-in-with-slack", + scopes: ["channels:read", "groups:read"], }); ``` @@ -48,18 +49,19 @@ export const withSlack = auth0AI.withTokenVault({ Wrap your tool using the Auth0 AI SDK to obtain an access token for the Slack API. -```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={2-4,8,14,18,30-32} -import { ErrorCode, WebClient } from "@slack/web-api"; +```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={3-5,9,15,19,31-33} +import { tool } from "ai"; +import { z } from "zod/v3"; + import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel"; import { TokenVaultError } from "@auth0/ai/interrupts"; -import { withSlack } from "@/lib/auth0-ai"; -import { tool } from "ai"; -import { z } from "zod"; +import { withSlack } from "@/agent/auth0-ai"; +import { ErrorCode, WebClient } from "@slack/web-api"; export const listChannels = withSlack( tool({ description: "List channels for the current user on Slack", - parameters: z.object({}), + inputSchema: z.object({}), execute: async () => { // Get the access token from Auth0 AI const accessToken = getAccessTokenFromTokenVault(); @@ -79,7 +81,7 @@ export const listChannels = withSlack( if (error && typeof error === "object" && "code" in error) { if (error.code === ErrorCode.HTTPError) { throw new TokenVaultError( - `Authorization required to access the Token Vault connection` + `Authorization required to access the Token Vault` ); } } @@ -101,76 +103,126 @@ Interrupts are a way for the system to pause execution and prompt the user to ta On the Chat agent class, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. -```typescript ./src/agent/chat.ts wrap lines highlight={1-2,52-54,63-66} -import { setAIContext } from "@auth0/ai-vercel"; -import { errorSerializer, withInterruptions } from "@auth0/ai-vercel/interrupts"; -// Other dependencies -import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; +```typescript ./src/agent/chat.ts wrap lines highlight={1-10,52,54-57,101} import { openai } from "@ai-sdk/openai"; +import { + AsyncUserConfirmationResumer, + CloudflareKVStore, +} from "@auth0/ai-cloudflare"; +import { + errorSerializer, + invokeTools, + withInterruptions, +} from "@auth0/ai-vercel/interrupts"; +import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; import { AIChatAgent } from "agents/ai-chat-agent"; +import { + convertToModelMessages, + createUIMessageStream, + createUIMessageStreamResponse, + generateId, + stepCountIs, + streamText, + type UIMessage, +} from "ai"; +import { executions, tools } from "./tools"; +import { processToolCalls } from "./utils"; + +const model = openai("gpt-4o-2024-11-20"); + +class BaseChat extends AIChatAgent {} + +const AuthedChat = AuthAgent(BaseChat); +const OwnedAuthedChat = OwnedAgent(AuthedChat); +const ResumableOwnedAuthedChat = AsyncUserConfirmationResumer(OwnedAuthedChat); + +export class Chat extends ResumableOwnedAuthedChat { + messages: UIMessage[] = []; + + declare mcp?: + | { + unstable_getAITools?: () => Record; + } + | undefined; -const SuperAgent = OwnedAgent(AuthAgent(AIChatAgent)); - -export class Chat extends SuperAgent { - async onChatMessage( - onFinish: StreamTextOnFinishCallback, - options?: { abortSignal?: AbortSignal } - ) { - // Collect all tools, including MCP tools + async onChatMessage() { const allTools = { ...tools, - ...this.mcp.unstable_getAITools(), + ...(this.mcp?.unstable_getAITools?.() ?? {}), }; - const claims = this.getClaims(); - // Create a streaming response that handles both text and tool outputs - const dataStreamResponse = createDataStreamResponse({ - execute: withInterruptions(async (dataStream) => { - // Process any pending tool calls from previous messages - // This handles human-in-the-loop confirmations for tools - const processedMessages = await processToolCalls({ - messages: this.messages, - dataStream, - tools: allTools, - executions, - }); - // Stream the AI response using GPT-4 - const result = streamText({ - model, - system: `You are a helpful assistant that can do various tasks... - -${unstable_getSchedulePrompt({ date: new Date() })} + const claims = this.getClaims?.(); + + const stream = createUIMessageStream({ + originalMessages: this.messages, + execute: withInterruptions( + async ({ writer }) => { + await invokeTools({ + messages: convertToModelMessages(this.messages), + tools: allTools, + }); + + const processed = await processToolCalls({ + messages: this.messages, + dataStream: writer, + tools: allTools, + executions, + }); + + const result = streamText({ + model, + stopWhen: stepCountIs(10), + messages: convertToModelMessages(processed), + system: `You are a helpful assistant that can do various tasks... If the user asks to schedule a task, use the schedule tool to schedule the task. -The name of the user is ${claims?.name ?? "unknown"}. -`, - messages: processedMessages, - tools: allTools, - onFinish: async (args) => { - onFinish( - args as Parameters>[0] - ); - }, - onError: (error) => { - if (!Auth0Interrupt.isInterrupt(error)) { - return; - } - console.error("Error while streaming:", error); - }, - maxSteps: 10, - }); - - // Merge the AI response stream with tool execution outputs - result.mergeIntoDataStream(dataStream); - }), - onError: errorSerializer((err) => { - console.log(err); - return "Oops, an error occured!"; - }), +The name of the user is ${claims?.name ?? "unknown"}.`, + tools: allTools, + onStepFinish: (output) => { + if (output.finishReason === "tool-calls") { + const last = output.content[output.content.length - 1]; + if (last?.type === "tool-error") { + const { toolName, toolCallId, error, input } = last; + const serializableError = { + cause: error, + toolCallId, + toolName, + toolArgs: input, + }; + throw serializableError; + } + } + }, + }); + + writer.merge( + result.toUIMessageStream({ + sendReasoning: true, + }) + ); + }, + { messages: this.messages, tools: allTools } + ), + onError: errorSerializer(), }); - return dataStreamResponse; + return createUIMessageStreamResponse({ stream }); + } + + async executeTask(description: string) { + await this.saveMessages([ + ...this.messages, + { + id: generateId(), + role: "user", + parts: [{ type: "text", text: `Running scheduled task: ${description}` }], + }, + ]); + } + + get auth0AIStore() { + return new CloudflareKVStore({ kv: (this as any).env.Session }); } } ``` From 28ec2c2e79266b4863b3f06b8aed2ec8ed675785 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Tue, 18 Nov 2025 15:55:38 -0500 Subject: [PATCH 2/9] feat: update Cloudflare quickstart snippets, How To prereq organization --- .../check-google-calendar-availability.mdx | 63 +++-- .../how-tos/get-salesforce-opportunities.mdx | 22 +- .../how-tos/list-github-repositories.mdx | 60 ++-- auth4genai/how-tos/list-slack-channels.mdx | 55 ++-- .../cloudflare-agents-js/call-your-api.mdx | 2 +- .../how-tos/github/cloudflare-agents.mdx | 261 ++++++++++++------ .../how-tos/github/github-prereqs-common.mdx | 5 + .../how-tos/github/github-prereqs-js.mdx | 4 + .../how-tos/github/github-prereqs-python.mdx | 3 + .../google-calendar/cloudflare-agents.mdx | 167 +++++++---- .../google-cal-prereqs-common.mdx | 7 + .../google-calendar/google-cal-prereqs-js.mdx | 5 + .../google-cal-prereqs-python.mdx | 3 + .../salesforce/salesforce-prereqs-common.mdx | 7 + .../salesforce/salesforce-prereqs-js.mdx | 7 + .../how-tos/slack/cloudflare-agents.mdx | 13 +- .../how-tos/slack/slack-prereqs-common.mdx | 5 + .../how-tos/slack/slack-prereqs-js.mdx | 3 + .../how-tos/slack/slack-prereqs-python.mdx | 3 + 19 files changed, 455 insertions(+), 240 deletions(-) create mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-common.mdx create mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-js.mdx create mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-python.mdx create mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx create mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx create mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx create mode 100644 auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx create mode 100644 auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx create mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx create mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx create mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx diff --git a/auth4genai/how-tos/check-google-calendar-availability.mdx b/auth4genai/how-tos/check-google-calendar-availability.mdx index 81e1b4e7b..6630c742c 100644 --- a/auth4genai/how-tos/check-google-calendar-availability.mdx +++ b/auth4genai/how-tos/check-google-calendar-availability.mdx @@ -12,48 +12,62 @@ import LlamaIndexJSSample from "/snippets/how-tos/google-calendar/llamaindex.mdx import LlamaIndexSample from "/snippets/how-tos/google-calendar/llamaindex-python.mdx"; import NextJSAuth0Sample from "/snippets/how-tos/google-calendar/nextjs-auth0.mdx"; import GenKitSample from "/snippets/how-tos/google-calendar/genkit.mdx"; + +import GoogleCalPrereqsCommon from "/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx"; +import GoogleCalPrereqsJs from "/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx"; +import GoogleCalPrereqsPython from "/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx"; import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; - - Before using this example, make sure you: - - - Install Node.js 18+ and `npm`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. - - Set up and configure a Google Cloud project: - - Enable the [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com). - - Create OAuth 2.0 credentials (Web Application) with proper redirect URIs. - - Configure a [Social Connection for Google in Auth0](https://marketplace.auth0.com/integrations/google-social-connection) - - Make sure to enable `Token Vault` - - Select `Offline Access` scope - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -61,23 +75,20 @@ import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx - - - Before using this example, make sure you: - - - Install Python 3.11+ and `pip`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Configure a [Social Connection for Google in Auth0](https://marketplace.auth0.com/integrations/google-social-connection) - - Make sure to enable `Token Vault` - - Select `Offline Access` scope - - + + + + + + + + diff --git a/auth4genai/how-tos/get-salesforce-opportunities.mdx b/auth4genai/how-tos/get-salesforce-opportunities.mdx index 71fcaa934..8091090f8 100644 --- a/auth4genai/how-tos/get-salesforce-opportunities.mdx +++ b/auth4genai/how-tos/get-salesforce-opportunities.mdx @@ -4,27 +4,15 @@ description: "Use OpenAI, NextJS, and the Auth0-AI SDKs to get Salesforce opport mode: "wide" --- +import SalesforcePrereqsCommon from "/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx"; +import SalesforcePrereqsJs from "/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx"; + - -Before using this example, make sure you: - -- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). -- Set up and configure a Salesforce instance with an [External Client App](https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&type=5). -- [Configure a Salesforce OIDC connection](https://auth0.com/docs/authenticate/identity-providers/enterprise-identity-providers/oidc) in Auth0 with the following scopes: - - `openid` - - `api` - - `refresh_token` - - `offline_access` -- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. -- Set the `SALESFORCE_INSTANCE_URL` in your `.env` file. - -```bash .env wrap lines -SALESFORCE_INSTANCE_URL=https://your-instance.salesforce.com -``` - + + ## 1. Define the Vercel AI tool and backend API route diff --git a/auth4genai/how-tos/list-github-repositories.mdx b/auth4genai/how-tos/list-github-repositories.mdx index 3c36fc0c2..143d9e2b2 100644 --- a/auth4genai/how-tos/list-github-repositories.mdx +++ b/auth4genai/how-tos/list-github-repositories.mdx @@ -13,45 +13,63 @@ import NextJSAuth0Sample from "/snippets/how-tos/github/nextjs-auth0.mdx"; import GenKitSample from "/snippets/how-tos/github/genkit.mdx"; import LlamaIndexJSSample from "/snippets/how-tos/github/llamaindex.mdx"; import AccountLinking from "/snippets/how-tos/account-linking.mdx"; + +import GithubPrereqsCommon from "/snippets/how-tos/github/github-prereqs-common.mdx"; +import GithubPrereqsJs from "/snippets/how-tos/github/github-prereqs-js.mdx"; +import GithubPrereqsPython from "/snippets/how-tos/github/github-prereqs-python.mdx"; + import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; - - Before using this example, make sure you: - - - Install Node.js 18+ and `npm`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. - - Create and configure a [GitHub App](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps). - - Configure a [Social Connection for GitHub in Auth0](https://marketplace.auth0.com/integrations/github-social-connection) - - Make sure to enable `Token Vault` - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -59,22 +77,20 @@ import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx - - - Before using this example, make sure you: - - - Install Python 3.11+ and `pip`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Configure a [Social Connection for GitHub in Auth0](https://marketplace.auth0.com/integrations/github-social-connection) - - Make sure to enable `Token Vault` - - + + + + + + + + diff --git a/auth4genai/how-tos/list-slack-channels.mdx b/auth4genai/how-tos/list-slack-channels.mdx index 78194ee81..a77c7cfec 100644 --- a/auth4genai/how-tos/list-slack-channels.mdx +++ b/auth4genai/how-tos/list-slack-channels.mdx @@ -13,38 +13,51 @@ import GenKitSample from "/snippets/how-tos/slack/genkit.mdx"; import LlamaIndexJSSample from "/snippets/how-tos/slack/llamaindex.mdx"; import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; +import GithubPrereqsCommon from "/snippets/how-tos/github/github-prereqs-common.mdx"; +import GithubPrereqsJs from "/snippets/how-tos/github/github-prereqs-js.mdx"; +import GithubPrereqsPython from "/snippets/how-tos/github/github-prereqs-python.mdx"; + - - - Before using this example, make sure you: - - - Install Node.js 18+ and `npm`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. - - Configure a [Social Connection for Slack in Auth0](https://marketplace.auth0.com/integrations/sign-in-with-slack) - - Make sure to enable `Token Vault` - - - + + + + + + + + + + + + + + + + + + + + + @@ -52,22 +65,20 @@ import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx - - - Before using this example, make sure you: - - - Install Python 3.11+ and `pip`. - - [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). - - Configure a [Social Connection for Slack in Auth0](https://marketplace.auth0.com/integrations/sign-in-with-slack) - - Make sure to enable `Token Vault` - - + + + + + + + + diff --git a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx index 4e57bea04..53c7b3d53 100644 --- a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx +++ b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx @@ -243,4 +243,4 @@ AI: You are Juan Martinez. Here are your details: - ......... That's it! You've successfully integrated first-party tool-calling into your project. -Explore [the start kit on GitHub](https://github.com/auth0-lab/cloudflare-agents-starter). +Explore [the starter kit on GitHub](https://github.com/auth0-lab/cloudflare-agents-starter). diff --git a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx index 24619446a..54e390fb2 100644 --- a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx @@ -40,7 +40,7 @@ const getAgent = () => { }; const refreshToken = async () => { - const credentials = (getAgent() as any).getCredentials(); + const credentials = getAgent().getCredentials(); return credentials?.refresh_token; }; @@ -55,13 +55,14 @@ export const withGitHub = auth0AI.withTokenVault({ Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API. -```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={6-7,9,15,19-21,28-30} +```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={7-8,10,16,20-22,29-31} import { tool } from "ai"; import { z } from "zod/v3"; import { Octokit, RequestError } from "octokit"; import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel"; import { TokenVaultError } from "@auth0/ai/interrupts"; +import { Octokit, RequestError } from "octokit"; import { withGitHub } from "@/agent/auth0-ai"; export const listRepositories = withGitHub( @@ -107,76 +108,122 @@ Interrupts are a way for the system to pause execution and prompt the user to ta On the Chat agent class, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. -```typescript ./src/agent/chat.ts wrap lines highlight={1-2,52-54,63-66} -import { setAIContext } from "@auth0/ai-vercel"; -import { errorSerializer, withInterruptions } from "@auth0/ai-vercel/interrupts"; -// Other dependencies -import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; +```typescript ./src/agent/chat.ts wrap lines highlight={4,6,46,70-84,95} import { openai } from "@ai-sdk/openai"; +import { CloudflareKVStore } from "@auth0/ai-cloudflare"; +import { + errorSerializer, + invokeTools, + withInterruptions, +} from "@auth0/ai-vercel/interrupts"; import { AIChatAgent } from "agents/ai-chat-agent"; +import { + convertToModelMessages, + createUIMessageStream, + createUIMessageStreamResponse, + generateId, + stepCountIs, + streamText, + type UIMessage, +} from "ai"; +import { extend } from "flumix"; +import { executions, tools } from "./tools"; +import { processToolCalls } from "./utils"; + +import { AsyncUserConfirmationResumer } from "@auth0/ai-cloudflare"; +import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; -const SuperAgent = OwnedAgent(AuthAgent(AIChatAgent)); +const model = openai("gpt-4o-2024-11-20"); + +const SuperAgent = extend(AIChatAgent) + .with(AuthAgent) + .with(OwnedAgent) + .with(AsyncUserConfirmationResumer) + .build(); export class Chat extends SuperAgent { - async onChatMessage( - onFinish: StreamTextOnFinishCallback, - options?: { abortSignal?: AbortSignal } - ) { - // Collect all tools, including MCP tools + messages: UIMessage[] = []; + + async onChatMessage() { const allTools = { ...tools, - ...this.mcp.unstable_getAITools(), + ...(this.mcp?.getAITools?.() ?? {}), }; - const claims = this.getClaims(); - // Create a streaming response that handles both text and tool outputs - const dataStreamResponse = createDataStreamResponse({ - execute: withInterruptions(async (dataStream) => { - // Process any pending tool calls from previous messages - // This handles human-in-the-loop confirmations for tools - const processedMessages = await processToolCalls({ - messages: this.messages, - dataStream, - tools: allTools, - executions, - }); - // Stream the AI response using GPT-4 - const result = streamText({ - model, - system: `You are a helpful assistant that can do various tasks... - -${unstable_getSchedulePrompt({ date: new Date() })} + const claims = this.getClaims?.(); + + const stream = createUIMessageStream({ + originalMessages: this.messages, + execute: withInterruptions( + async ({ writer }) => { + await invokeTools({ + messages: convertToModelMessages(this.messages), + tools: allTools, + }); + + const processed = await processToolCalls({ + messages: this.messages, + dataStream: writer, + tools: allTools, + executions, + }); + + const result = streamText({ + model, + stopWhen: stepCountIs(10), + messages: convertToModelMessages(processed), + system: `You are a helpful assistant that can do various tasks... If the user asks to schedule a task, use the schedule tool to schedule the task. -The name of the user is ${claims?.name ?? "unknown"}. -`, - messages: processedMessages, - tools: allTools, - onFinish: async (args) => { - onFinish( - args as Parameters>[0] - ); - }, - onError: (error) => { - if (!Auth0Interrupt.isInterrupt(error)) { - return; - } - console.error("Error while streaming:", error); - }, - maxSteps: 10, - }); - - // Merge the AI response stream with tool execution outputs - result.mergeIntoDataStream(dataStream); - }), - onError: errorSerializer((err) => { - console.log(err); - return "Oops, an error occured!"; - }), +The name of the user is ${claims?.name ?? "unknown"}.`, + tools: allTools, + onStepFinish: (output) => { + if (output.finishReason === "tool-calls") { + const last = output.content[output.content.length - 1]; + if (last?.type === "tool-error") { + const { toolName, toolCallId, error, input } = last; + const serializableError = { + cause: error, + toolCallId, + toolName, + toolArgs: input, + }; + throw serializableError; + } + } + }, + }); + + writer.merge( + result.toUIMessageStream({ + sendReasoning: true, + }) + ); + }, + { messages: this.messages, tools: allTools } + ), + onError: errorSerializer(), }); - return dataStreamResponse; + return createUIMessageStreamResponse({ stream }); + } + + async executeTask(description: string) { + await this.saveMessages([ + ...this.messages, + { + id: generateId(), + role: "user", + parts: [ + { type: "text", text: `Running scheduled task: ${description}` }, + ], + }, + ]); + } + + get auth0AIStore() { + return new CloudflareKVStore({ kv: this.env.Session }); } } ``` @@ -191,7 +238,7 @@ npx @auth0/ai-components add TokenVault Then, you can integrate the authentication popup in your chat component, using the interruptions helper from the SDK: -```tsx ./src/components/chat.tsx wrap lines highlight={4-6,17-18,33-42} +```tsx ./src/client/app.tsx wrap lines highlight={4-6,14-17,24,48-71} "use client"; import { useChat } from "@ai-sdk/react"; @@ -200,45 +247,85 @@ import { TokenVaultInterrupt } from "@auth0/ai/interrupts"; import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup"; export default function Chat() { + const agent = useAgent({ + agent: "chat", + name: threadID ?? undefined, + }); + + const chat = useAgentChatInterruptions({ + agent, + id: threadID, + }); const { messages: agentMessages, - input: agentInput, - handleInputChange: handleAgentInputChange, - handleSubmit: handleAgentSubmit, + sendMessage: handleAgentSubmit, addToolResult, clearHistory, toolInterrupt, - } = useAgentChatInterruptions({ - agent, - maxSteps: 5, - id: threadID, - }); + } = chat; return ( -
- {messages.map((message) => ( -
- {message.role === "user" ? "User: " : "AI: "} - {message.content} -
- ))} - - {TokenVaultInterrupt.isInterrupt(toolInterrupt) && ( - - )} + + {agentMessages.map((m: UIMessage, index) => { + const isUser = m.role === "user"; + + return ( +
+ {showDebug && ( +
+                {JSON.stringify(m, null, 2)}
+              
+ )} +
+
+
+
+ {m.parts?.map((part: any, i) => { + if ( + part?.type?.startsWith("tool-") && + toolInterrupt && + TokenVaultInterrupt.isInterrupt(toolInterrupt) + ) { + return ( + + +
+ ), + title: "Google Calendar Access", + description: + "We need access to your google Calendar in order to call this tool...", + action: { label: "Grant" }, + }} + /> + ); + } +
+
+
+
+
+ ); + })}
- setInput(e.target.value)} /> + setInput(e.target.value)} + />
- + ); } ``` diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx new file mode 100644 index 000000000..41e656736 --- /dev/null +++ b/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx @@ -0,0 +1,5 @@ +- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). +- Create and configure a [GitHub App](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps). +- Configure a [Social Connection for GitHub in Auth0](https://marketplace.auth0.com/integrations/github-social-connection) + - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. + - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx new file mode 100644 index 000000000..5937459c6 --- /dev/null +++ b/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx @@ -0,0 +1,4 @@ +Before using this example, make sure you: + +- Install Node.js 18+ and `npm`. +- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx new file mode 100644 index 000000000..64c2b8def --- /dev/null +++ b/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx @@ -0,0 +1,3 @@ +Before using this example, make sure you: + +- Install Python 3.11+ and `pip`. diff --git a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx index 6e0665d3b..bfcd5bbe8 100644 --- a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx @@ -1,14 +1,18 @@ ### 1. Configure Auth0 AI -If you started from the [Auth0 Cloudflare Agents starter kit](https://github.com/auth0-lab/cloudflare-agents-starter), you can skip this step as the Auth0 AI SDK is already configured. + + If you started from the [Auth0 Cloudflare Agents starter + kit](https://github.com/auth0-lab/cloudflare-agents-starter), you can skip + this step as the Auth0 AI SDK is already configured. + First, you must configure your Cloudflare Agent to use Auth0 and both in the Worker and in the Chat Agent itself. We recommend the following two sdks: + - [Auth0 Hono Web SDK](https://github.com/auth0-lab/auth0-hono): for the Worker. - [Auth0 Cloudflare Agents API SDK](https://github.com/auth0-lab/auth0-cloudflare-agents-api) for the Chat Agent. You can also check our [Starter Kit](https://github.com/auth0-lab/cloudflare-agents-starter) to understand how to configure this. - Then, you need to install the Auth0 AI SDK for Cloudflare Agents: ```bash wrap lines @@ -32,13 +36,19 @@ const getAgent = () => { setGlobalAIContext(() => ({ threadID: getAgent().name })); -const auth0AI = new Auth0AI(); +const auth0AI = new Auth0AI({ + store: () => { + return getAgent().auth0AIStore; + }, +}); + +const refreshToken = async () => { + const credentials = getAgent().getCredentials(); + return credentials?.refresh_token; +}; export const withGoogleCalendar = auth0AI.withTokenVault({ - refreshToken: async () => { - const credentials = (getAgent() as any).getCredentials(); - return credentials?.refresh_token; - }, + refreshToken, connection: "google-oauth2", scopes: ["https://www.googleapis.com/auth/calendar.freebusy"], }); @@ -108,24 +118,24 @@ export const checkUsersCalendar = withGoogleCalendar( Interrupts are a way for the system to pause execution and prompt the user to take an action—such as authenticating or granting API access—before resuming the interaction. This ensures that any required access is granted dynamically and securely during the chat experience. In this context, Auth0-AI SDK manages authentication redirects in the Vercel AI SDK via these interrupts. -If you started from the [Auth0 Cloudflare Agents starter kit](https://github.com/auth0-lab/cloudflare-agents-starter), you can skip this section as the Auth0 AI SDK is already configured to handle interrupts. + + If you started from the [Auth0 Cloudflare Agents starter + kit](https://github.com/auth0-lab/cloudflare-agents-starter), you can skip + this section as the Auth0 AI SDK is already configured to handle interrupts. + #### Server Side On the Chat agent class, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. -```typescript ./src/agent/chat.ts wrap lines highlight={6-10,52,54-57,101} +```typescript ./src/agent/chat.ts wrap lines highlight={5-6,46,70-84,95} import { openai } from "@ai-sdk/openai"; -import { - AsyncUserConfirmationResumer, - CloudflareKVStore, -} from "@auth0/ai-cloudflare"; +import { CloudflareKVStore } from "@auth0/ai-cloudflare"; import { errorSerializer, invokeTools, withInterruptions, } from "@auth0/ai-vercel/interrupts"; -import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; import { AIChatAgent } from "agents/ai-chat-agent"; import { convertToModelMessages, @@ -136,30 +146,28 @@ import { streamText, type UIMessage, } from "ai"; +import { extend } from "flumix"; import { executions, tools } from "./tools"; import { processToolCalls } from "./utils"; -const model = openai("gpt-4o-2024-11-20"); +import { AsyncUserConfirmationResumer } from "@auth0/ai-cloudflare"; +import { AuthAgent, OwnedAgent } from "@auth0/auth0-cloudflare-agents-api"; -class BaseChat extends AIChatAgent {} +const model = openai("gpt-4o-2024-11-20"); -const AuthedChat = AuthAgent(BaseChat); -const OwnedAuthedChat = OwnedAgent(AuthedChat); -const ResumableOwnedAuthedChat = AsyncUserConfirmationResumer(OwnedAuthedChat); +const SuperAgent = extend(AIChatAgent) + .with(AuthAgent) + .with(OwnedAgent) + .with(AsyncUserConfirmationResumer) + .build(); -export class Chat extends ResumableOwnedAuthedChat { +export class Chat extends SuperAgent { messages: UIMessage[] = []; - declare mcp?: - | { - unstable_getAITools?: () => Record; - } - | undefined; - async onChatMessage() { const allTools = { ...tools, - ...(this.mcp?.unstable_getAITools?.() ?? {}), + ...(this.mcp?.getAITools?.() ?? {}), }; const claims = this.getClaims?.(); @@ -227,16 +235,17 @@ The name of the user is ${claims?.name ?? "unknown"}.`, { id: generateId(), role: "user", - parts: [{ type: "text", text: `Running scheduled task: ${description}` }], + parts: [ + { type: "text", text: `Running scheduled task: ${description}` }, + ], }, ]); } get auth0AIStore() { - return new CloudflareKVStore({ kv: (this as any).env.Session }); + return new CloudflareKVStore({ kv: this.env.Session }); } } - ``` #### Client Side @@ -249,7 +258,7 @@ npx @auth0/ai-components add TokenVault Then, you can integrate the authentication popup in your chat component, using the interruptions helper from the SDK: -```tsx ./src/components/chat.tsx wrap lines highlight={4-6,17-18,33-42} +```tsx ./src/client/app.tsx wrap lines highlight={4-6,14-17,24,48-71} "use client"; import { useChat } from "@ai-sdk/react"; @@ -258,45 +267,85 @@ import { TokenVaultInterrupt } from "@auth0/ai/interrupts"; import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup"; export default function Chat() { + const agent = useAgent({ + agent: "chat", + name: threadID ?? undefined, + }); + + const chat = useAgentChatInterruptions({ + agent, + id: threadID, + }); const { messages: agentMessages, - input: agentInput, - handleInputChange: handleAgentInputChange, - handleSubmit: handleAgentSubmit, + sendMessage: handleAgentSubmit, addToolResult, clearHistory, toolInterrupt, - } = useAgentChatInterruptions({ - agent, - maxSteps: 5, - id: threadID, - }); + } = chat; return ( -
- {messages.map((message) => ( -
- {message.role === "user" ? "User: " : "AI: "} - {message.content} -
- ))} - - {TokenVaultInterrupt.isInterrupt(toolInterrupt) && ( - - )} + + {agentMessages.map((m: UIMessage, index) => { + const isUser = m.role === "user"; + + return ( +
+ {showDebug && ( +
+                {JSON.stringify(m, null, 2)}
+              
+ )} +
+
+
+
+ {m.parts?.map((part: any, i) => { + if ( + part?.type?.startsWith("tool-") && + toolInterrupt && + TokenVaultInterrupt.isInterrupt(toolInterrupt) + ) { + return ( + + +
+ ), + title: "Google Calendar Access", + description: + "We need access to your google Calendar in order to call this tool...", + action: { label: "Grant" }, + }} + /> + ); + } +
+
+
+
+
+ ); + })}
- setInput(e.target.value)} /> + setInput(e.target.value)} + />
- + ); } ``` diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx new file mode 100644 index 000000000..98787b105 --- /dev/null +++ b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx @@ -0,0 +1,7 @@ +- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). +- Set up and configure a Google Cloud project: + - Enable the [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com). + - Create OAuth 2.0 credentials (Web Application) with proper redirect URIs. +- Configure a [Social Connection for Google in Auth0](https://marketplace.auth0.com/integrations/google-social-connection) + - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. + - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx new file mode 100644 index 000000000..9f064efbb --- /dev/null +++ b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx @@ -0,0 +1,5 @@ +Before using this example, make sure you: + +- Install Node.js 18+ and `npm`. +- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. + diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx new file mode 100644 index 000000000..64c2b8def --- /dev/null +++ b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx @@ -0,0 +1,3 @@ +Before using this example, make sure you: + +- Install Python 3.11+ and `pip`. diff --git a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx new file mode 100644 index 000000000..5950968a4 --- /dev/null +++ b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx @@ -0,0 +1,7 @@ +- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). +- Set up and configure a Salesforce instance with an [External Client App](https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&type=5). +- [Configure a Salesforce OIDC connection](https://auth0.com/docs/authenticate/identity-providers/enterprise-identity-providers/oidc) in Auth0 with the following scopes: + - `openid` + - `api` + - `refresh_token` + - `offline_access` diff --git a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx new file mode 100644 index 000000000..068675408 --- /dev/null +++ b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx @@ -0,0 +1,7 @@ +Before using this example, make sure you: +- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. +- Set the `SALESFORCE_INSTANCE_URL` in your `.env` file. + +```bash .env wrap lines +SALESFORCE_INSTANCE_URL=https://your-instance.salesforce.com +``` diff --git a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx index 057414669..c16085217 100644 --- a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx @@ -34,12 +34,13 @@ setGlobalAIContext(() => ({ threadID: getAgent().name })); const auth0AI = new Auth0AI(); +const refreshToken = async () => { + const credentials = getAgent().getCredentials(); + return credentials?.refresh_token; +}; + export const withSlack = auth0AI.withTokenVault({ - refreshToken: async () => { - const session = await auth0AI.getSession(); - const refreshToken = session?.tokenSet.refreshToken as string; - return refreshToken; - }, + refreshToken, connection: "sign-in-with-slack", scopes: ["channels:read", "groups:read"], }); @@ -49,7 +50,7 @@ export const withSlack = auth0AI.withTokenVault({ Wrap your tool using the Auth0 AI SDK to obtain an access token for the Slack API. -```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={3-5,9,15,19,31-33} +```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={4-5,9,15,19,31-33} import { tool } from "ai"; import { z } from "zod/v3"; diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx new file mode 100644 index 000000000..a25a54183 --- /dev/null +++ b/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx @@ -0,0 +1,5 @@ +- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). +- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. +- Configure a [Social Connection for Slack in Auth0](https://marketplace.auth0.com/integrations/sign-in-with-slack) + - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. + - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx new file mode 100644 index 000000000..e7d365dd3 --- /dev/null +++ b/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx @@ -0,0 +1,3 @@ +Before using this example, make sure you: + +- Install Node.js 18+ and `npm`. diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx new file mode 100644 index 000000000..64c2b8def --- /dev/null +++ b/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx @@ -0,0 +1,3 @@ +Before using this example, make sure you: + +- Install Python 3.11+ and `pip`. From a18701577beb960abdd9b4c46c3a50e542cbe541 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 13:00:59 -0500 Subject: [PATCH 3/9] feat: refactor how-to pre-reqs --- .../check-google-calendar-availability.mdx | 47 +---- .../how-tos/get-salesforce-opportunities.mdx | 8 +- .../how-tos/list-github-repositories.mdx | 47 +---- auth4genai/how-tos/list-slack-channels.mdx | 41 +---- .../common/create-custom-api-client.mdx | 27 --- .../how-tos/github/github-prereqs-common.mdx | 5 - .../how-tos/github/github-prereqs-js.mdx | 4 - .../how-tos/github/github-prereqs-python.mdx | 3 - .../snippets/how-tos/github/prereqs.jsx | 147 ++++++++++++++++ .../google-cal-prereqs-common.mdx | 7 - .../google-calendar/google-cal-prereqs-js.mdx | 5 - .../google-cal-prereqs-python.mdx | 3 - .../how-tos/google-calendar/prereqs.jsx | 151 ++++++++++++++++ .../snippets/how-tos/salesforce/prereqs.jsx | 164 ++++++++++++++++++ .../salesforce/salesforce-prereqs-common.mdx | 7 - .../salesforce/salesforce-prereqs-js.mdx | 7 - auth4genai/snippets/how-tos/slack/prereqs.jsx | 128 ++++++++++++++ .../how-tos/slack/slack-prereqs-common.mdx | 5 - .../how-tos/slack/slack-prereqs-js.mdx | 3 - .../how-tos/slack/slack-prereqs-python.mdx | 3 - 20 files changed, 618 insertions(+), 194 deletions(-) delete mode 100644 auth4genai/snippets/common/create-custom-api-client.mdx delete mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-common.mdx delete mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-js.mdx delete mode 100644 auth4genai/snippets/how-tos/github/github-prereqs-python.mdx create mode 100644 auth4genai/snippets/how-tos/github/prereqs.jsx delete mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx delete mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx delete mode 100644 auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx create mode 100644 auth4genai/snippets/how-tos/google-calendar/prereqs.jsx create mode 100644 auth4genai/snippets/how-tos/salesforce/prereqs.jsx delete mode 100644 auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx delete mode 100644 auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx create mode 100644 auth4genai/snippets/how-tos/slack/prereqs.jsx delete mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx delete mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx delete mode 100644 auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx diff --git a/auth4genai/how-tos/check-google-calendar-availability.mdx b/auth4genai/how-tos/check-google-calendar-availability.mdx index 6630c742c..c52570599 100644 --- a/auth4genai/how-tos/check-google-calendar-availability.mdx +++ b/auth4genai/how-tos/check-google-calendar-availability.mdx @@ -13,61 +13,38 @@ import LlamaIndexSample from "/snippets/how-tos/google-calendar/llamaindex-pytho import NextJSAuth0Sample from "/snippets/how-tos/google-calendar/nextjs-auth0.mdx"; import GenKitSample from "/snippets/how-tos/google-calendar/genkit.mdx"; -import GoogleCalPrereqsCommon from "/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx"; -import GoogleCalPrereqsJs from "/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx"; -import GoogleCalPrereqsPython from "/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx"; -import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; +import { GoogleCalendarPrereqs } from "/snippets/how-tos/google-calendar/prereqs.jsx"; - - - - - + - - - - + - - - - - + - - - - + - - - - + - - - - + @@ -77,18 +54,12 @@ import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx - - - - + - - - - + diff --git a/auth4genai/how-tos/get-salesforce-opportunities.mdx b/auth4genai/how-tos/get-salesforce-opportunities.mdx index 8091090f8..2f0edeb7b 100644 --- a/auth4genai/how-tos/get-salesforce-opportunities.mdx +++ b/auth4genai/how-tos/get-salesforce-opportunities.mdx @@ -4,16 +4,12 @@ description: "Use OpenAI, NextJS, and the Auth0-AI SDKs to get Salesforce opport mode: "wide" --- -import SalesforcePrereqsCommon from "/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx"; -import SalesforcePrereqsJs from "/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx"; +import { SalesforcePrereqs } from "/snippets/how-tos/salesforce/prereqs.jsx"; - - - - + ## 1. Define the Vercel AI tool and backend API route diff --git a/auth4genai/how-tos/list-github-repositories.mdx b/auth4genai/how-tos/list-github-repositories.mdx index 143d9e2b2..f94ea12fa 100644 --- a/auth4genai/how-tos/list-github-repositories.mdx +++ b/auth4genai/how-tos/list-github-repositories.mdx @@ -14,62 +14,39 @@ import GenKitSample from "/snippets/how-tos/github/genkit.mdx"; import LlamaIndexJSSample from "/snippets/how-tos/github/llamaindex.mdx"; import AccountLinking from "/snippets/how-tos/account-linking.mdx"; -import GithubPrereqsCommon from "/snippets/how-tos/github/github-prereqs-common.mdx"; -import GithubPrereqsJs from "/snippets/how-tos/github/github-prereqs-js.mdx"; -import GithubPrereqsPython from "/snippets/how-tos/github/github-prereqs-python.mdx"; - -import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; +import { GitHubPrereqs } from "/snippets/how-tos/github/prereqs.jsx"; - - - - + - - - - + - - - - - + - - - - + - - - - + - - - - + @@ -79,18 +56,12 @@ import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx - - - - + - - - - + diff --git a/auth4genai/how-tos/list-slack-channels.mdx b/auth4genai/how-tos/list-slack-channels.mdx index a77c7cfec..072ae2435 100644 --- a/auth4genai/how-tos/list-slack-channels.mdx +++ b/auth4genai/how-tos/list-slack-channels.mdx @@ -11,53 +11,34 @@ import LangGraphSample from "/snippets/how-tos/slack/langgraph-python.mdx"; import LlamaIndexSample from "/snippets/how-tos/slack/llamaindex-python.mdx"; import GenKitSample from "/snippets/how-tos/slack/genkit.mdx"; import LlamaIndexJSSample from "/snippets/how-tos/slack/llamaindex.mdx"; -import CreateCustomApiClient from "/snippets/common/create-custom-api-client.mdx"; -import GithubPrereqsCommon from "/snippets/how-tos/github/github-prereqs-common.mdx"; -import GithubPrereqsJs from "/snippets/how-tos/github/github-prereqs-js.mdx"; -import GithubPrereqsPython from "/snippets/how-tos/github/github-prereqs-python.mdx"; +import { SlackPrereqs } from "/snippets/how-tos/slack/prereqs.jsx"; - - - - + - - - - + - - - - - + - - - - + - - - - + @@ -67,18 +48,12 @@ import GithubPrereqsPython from "/snippets/how-tos/github/github-prereqs-python. - - - - + - - - - + diff --git a/auth4genai/snippets/common/create-custom-api-client.mdx b/auth4genai/snippets/common/create-custom-api-client.mdx deleted file mode 100644 index 24cb33858..000000000 --- a/auth4genai/snippets/common/create-custom-api-client.mdx +++ /dev/null @@ -1,27 +0,0 @@ -- Create a Custom API Client: -
- The Custom API Client allows your API server to perform token - exchanges using access tokens - instead of refresh tokens. - This client enables Token Vault to exchange an access token for an - external API access token (e.g., Google Calendar API). -
-
    -
  • - Navigate to Applications > APIs -
  • -
  • - Click the{" "} - Create API button to create a new Custom API. -
  • -
  • - Go to the Custom API you created and click the Add Application button in the right top corner. -
  • -
  • - After that click the Configure Application button in the right top corner. -
  • -
  • - Note down the client id and client secret{" "} - for your environment variables. -
  • -
diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx deleted file mode 100644 index 41e656736..000000000 --- a/auth4genai/snippets/how-tos/github/github-prereqs-common.mdx +++ /dev/null @@ -1,5 +0,0 @@ -- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). -- Create and configure a [GitHub App](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps). -- Configure a [Social Connection for GitHub in Auth0](https://marketplace.auth0.com/integrations/github-social-connection) - - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. - - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx deleted file mode 100644 index 5937459c6..000000000 --- a/auth4genai/snippets/how-tos/github/github-prereqs-js.mdx +++ /dev/null @@ -1,4 +0,0 @@ -Before using this example, make sure you: - -- Install Node.js 18+ and `npm`. -- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. diff --git a/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx b/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx deleted file mode 100644 index 64c2b8def..000000000 --- a/auth4genai/snippets/how-tos/github/github-prereqs-python.mdx +++ /dev/null @@ -1,3 +0,0 @@ -Before using this example, make sure you: - -- Install Python 3.11+ and `pip`. diff --git a/auth4genai/snippets/how-tos/github/prereqs.jsx b/auth4genai/snippets/how-tos/github/prereqs.jsx new file mode 100644 index 000000000..edd74b9b4 --- /dev/null +++ b/auth4genai/snippets/how-tos/github/prereqs.jsx @@ -0,0 +1,147 @@ +export const GitHubPrereqs = ({ + lang, + createCustomApiClientStep = false, +}) => { + const languageSteps = []; + + if (lang === "js") { + languageSteps.push( + + Install Node.js 20+ and npm + + } + />, + ); + } else if (lang === "python") { + languageSteps.push( + + Install Python 3.11+ and pip + + } + /> + ); + } + + const commonSteps = [ + + Complete the{" "} + + User authentication quickstart + {" "} + to create an application integrated with Auth0. + + } + />, + + + Set up an OpenAI API key + + . + + } + />, + + Create and configure a{" "} + + GitHub App + + . + + } + />, + + Configure a{" "} + + Social Connection for GitHub in Auth0 + + + } + > +
    +
  • + Under the Purpose section, make sure to enable{" "} + Use for Authentication and{" "} + Use for Connected Accounts with Token Vault toggles. +
  • +
  • + Under the Permissions section, enable the Offline Access{" "} + scope. +
  • +
+
, + ]; + + if (createCustomApiClientStep) { + commonSteps.push( + + The Custom API Client allows your API server to perform token exchanges + using{" "} + + access tokens + {" "} + instead of{" "} + + refresh tokens + + . This client enables Token Vault to exchange an access token for an + external API access token (e.g., GitHub API). +
+
    +
  • + Navigate to Applications > APIs +
  • +
  • + Click the Create API button to create a new Custom + API. +
  • +
  • + Go to the Custom API you created and click the{" "} + Add Application button in the right top corner. +
  • +
  • + After that click the Configure Application button + in the right top corner. +
  • +
  • + Note down the client id and client secret{" "} + for your environment variables. +
  • +
+
+ ); + } + + return ( + <> + + Prerequisites + + Before getting started, make sure you have completed the following steps: + {[...languageSteps, ...commonSteps]} + + ); +}; diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx deleted file mode 100644 index 98787b105..000000000 --- a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-common.mdx +++ /dev/null @@ -1,7 +0,0 @@ -- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). -- Set up and configure a Google Cloud project: - - Enable the [Google Calendar API](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com). - - Create OAuth 2.0 credentials (Web Application) with proper redirect URIs. -- Configure a [Social Connection for Google in Auth0](https://marketplace.auth0.com/integrations/google-social-connection) - - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. - - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx deleted file mode 100644 index 9f064efbb..000000000 --- a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-js.mdx +++ /dev/null @@ -1,5 +0,0 @@ -Before using this example, make sure you: - -- Install Node.js 18+ and `npm`. -- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. - diff --git a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx b/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx deleted file mode 100644 index 64c2b8def..000000000 --- a/auth4genai/snippets/how-tos/google-calendar/google-cal-prereqs-python.mdx +++ /dev/null @@ -1,3 +0,0 @@ -Before using this example, make sure you: - -- Install Python 3.11+ and `pip`. diff --git a/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx new file mode 100644 index 000000000..f2f0c2e95 --- /dev/null +++ b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx @@ -0,0 +1,151 @@ +export const GoogleCalendarPrereqs = ({ + lang, + createCustomApiClientStep = false, +}) => { + const languageSteps = []; + + if (lang === "js") { + languageSteps.push( + + Install Node.js 20+ and npm + + } + />, + ); + } else if (lang === "python") { + languageSteps.push( + + Install Python 3.11+ and pip + + } + /> + ); + } + + const commonSteps = [ + + Complete the{" "} + + User authentication quickstart + {" "} + to create an application integrated with Auth0. + + } + />, + + + Set up an OpenAI API key + + . + + } + />, + +
    +
  • + Enable the{" "} + + Google Calendar API + + . +
  • +
  • + Create OAuth 2.0 credentials (Web Application) with proper redirect + URIs. +
  • +
+
, + + Configure a{" "} + + Social Connection for Google in Auth0 + + + } + > +
    +
  • + Under the Purpose section, make sure to enable{" "} + Use for Authentication and{" "} + Use for Connected Accounts with Token Vault toggles. +
  • +
  • + Under the Permissions section, enable the Offline Access{" "} + scope. +
  • +
+
, + ]; + + if (createCustomApiClientStep) { + commonSteps.push( + + The Custom API Client allows your API server to perform token exchanges + using{" "} + + access tokens + {" "} + instead of{" "} + + refresh tokens + + . This client enables Token Vault to exchange an access token for an + external API access token (e.g., Google Calendar API). +
+
    +
  • + Navigate to Applications > APIs +
  • +
  • + Click the Create API button to create a new Custom + API. +
  • +
  • + Go to the Custom API you created and click the{" "} + Add Application button in the right top corner. +
  • +
  • + After that click the Configure Application button + in the right top corner. +
  • +
  • + Note down the client id and client secret{" "} + for your environment variables. +
  • +
+
+ ); + } + + return ( + <> + + Prerequisites + + Before getting started, make sure you have completed the following steps: + {[...languageSteps, ...commonSteps]} + + ); +}; diff --git a/auth4genai/snippets/how-tos/salesforce/prereqs.jsx b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx new file mode 100644 index 000000000..19bcd4e70 --- /dev/null +++ b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx @@ -0,0 +1,164 @@ +export const SalesforcePrereqs = ({ + lang, + createCustomApiClientStep = false, +}) => { + const languageSteps = []; + + if (lang === "js") { + languageSteps.push( + + Install Node.js 20+ and npm + + } + />, + ); + } else if (lang === "python") { + languageSteps.push( + + Install Python 3.11+ and pip + + } + /> + ); + } + + const commonSteps = [ + + Complete the{" "} + + User authentication quickstart + {" "} + to create an application integrated with Auth0. + + } + />, + + + Set up an OpenAI API key + + . + + } + />, + + Set up and configure a Salesforce instance with an{" "} + + External Client App + + . + + } + />, + + Set the SALESFORCE_INSTANCE_URL in your .env{" "} + file + + } + > +
+        
+          SALESFORCE_INSTANCE_URL=https://your-instance.salesforce.com
+        
+      
+
, + + + Configure a Salesforce OIDC connection + {" "} + in Auth0 with the following scopes + + } + > +
    +
  • + openid +
  • +
  • + api +
  • +
  • + refresh_token +
  • +
  • + offline_access +
  • +
+
, + ]; + + if (createCustomApiClientStep) { + commonSteps.push( + + The Custom API Client allows your API server to perform token exchanges + using{" "} + + access tokens + {" "} + instead of{" "} + + refresh tokens + + . This client enables Token Vault to exchange an access token for an + external API access token (e.g., Salesforce API). +
+
    +
  • + Navigate to Applications > APIs +
  • +
  • + Click the Create API button to create a new Custom + API. +
  • +
  • + Go to the Custom API you created and click the{" "} + Add Application button in the right top corner. +
  • +
  • + After that click the Configure Application button + in the right top corner. +
  • +
  • + Note down the client id and client secret{" "} + for your environment variables. +
  • +
+
+ ); + } + + return ( + <> + + Prerequisites + + Before getting started, make sure you have completed the following steps: + {[...languageSteps, ...commonSteps]} + + ); +}; diff --git a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx deleted file mode 100644 index 5950968a4..000000000 --- a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-common.mdx +++ /dev/null @@ -1,7 +0,0 @@ -- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). -- Set up and configure a Salesforce instance with an [External Client App](https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&type=5). -- [Configure a Salesforce OIDC connection](https://auth0.com/docs/authenticate/identity-providers/enterprise-identity-providers/oidc) in Auth0 with the following scopes: - - `openid` - - `api` - - `refresh_token` - - `offline_access` diff --git a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx b/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx deleted file mode 100644 index 068675408..000000000 --- a/auth4genai/snippets/how-tos/salesforce/salesforce-prereqs-js.mdx +++ /dev/null @@ -1,7 +0,0 @@ -Before using this example, make sure you: -- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. -- Set the `SALESFORCE_INSTANCE_URL` in your `.env` file. - -```bash .env wrap lines -SALESFORCE_INSTANCE_URL=https://your-instance.salesforce.com -``` diff --git a/auth4genai/snippets/how-tos/slack/prereqs.jsx b/auth4genai/snippets/how-tos/slack/prereqs.jsx new file mode 100644 index 000000000..3380e9b1a --- /dev/null +++ b/auth4genai/snippets/how-tos/slack/prereqs.jsx @@ -0,0 +1,128 @@ +export const SlackPrereqs = ({ + lang, + createCustomApiClientStep = false, +}) => { + const languageSteps = []; + + if (lang === "js") { + languageSteps.push( + + Install Node.js 20+ and npm + + } + /> + ); + } else if (lang === "python") { + languageSteps.push( + + Install Python 3.11+ and pip + + } + /> + ); + } + + const commonSteps = [ + + Complete the{" "} + + User authentication quickstart + {" "} + to create an application integrated with Auth0. + + } + />, + + + Set up an OpenAI API key + + . + + } + />, + + Configure a{" "} + + Social Connection for Slack in Auth0 + + + } + > +
    +
  • + Under the Purpose section, make sure to enable{" "} + Use for Authentication and{" "} + Use for Connected Accounts with Token Vault toggles. +
  • +
+
, + ]; + + if (createCustomApiClientStep) { + commonSteps.push( + + The Custom API Client allows your API server to perform token exchanges + using{" "} + + access tokens + {" "} + instead of{" "} + + refresh tokens + + . This client enables Token Vault to exchange an access token for an + external API access token (e.g., Slack API). +
+
    +
  • + Navigate to Applications > APIs +
  • +
  • + Click the Create API button to create a new Custom + API. +
  • +
  • + Go to the Custom API you created and click the{" "} + Add Application button in the right top corner. +
  • +
  • + After that click the Configure Application button + in the right top corner. +
  • +
  • + Note down the client id and client secret{" "} + for your environment variables. +
  • +
+
+ ); + } + + return ( + <> + + Prerequisites + + Before getting started, make sure you have completed the following steps: + {[...languageSteps, ...commonSteps]} + + ); +}; diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx deleted file mode 100644 index a25a54183..000000000 --- a/auth4genai/snippets/how-tos/slack/slack-prereqs-common.mdx +++ /dev/null @@ -1,5 +0,0 @@ -- [Set up an OpenAI API key](https://platform.openai.com/docs/quickstart?api-mode=chat). -- Complete the [User authentication quickstart](/get-started/user-authentication) to create a Next.js app integrated with Auth0. -- Configure a [Social Connection for Slack in Auth0](https://marketplace.auth0.com/integrations/sign-in-with-slack) - - Under the Purpose section, make sure to enable `User for Authentication` and `Use for Connected Accounts with Token Vault` toggles. - - Under the Permissions section, enable `Offline Access` scope. diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx deleted file mode 100644 index e7d365dd3..000000000 --- a/auth4genai/snippets/how-tos/slack/slack-prereqs-js.mdx +++ /dev/null @@ -1,3 +0,0 @@ -Before using this example, make sure you: - -- Install Node.js 18+ and `npm`. diff --git a/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx b/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx deleted file mode 100644 index 64c2b8def..000000000 --- a/auth4genai/snippets/how-tos/slack/slack-prereqs-python.mdx +++ /dev/null @@ -1,3 +0,0 @@ -Before using this example, make sure you: - -- Install Python 3.11+ and `pip`. From acf96380bd42865118eaa960b06cfeb5b11e8111 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 13:21:47 -0500 Subject: [PATCH 4/9] fix: updated cloudflare github repo consent snippet --- auth4genai/snippets/how-tos/github/cloudflare-agents.mdx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx index 54e390fb2..486fffc80 100644 --- a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx @@ -297,14 +297,9 @@ export default function Chat() { interrupt={toolInterrupt} auth={{ authorizePath: "/auth/login" }} connectWidget={{ - icon: ( -
- -
- ), - title: "Google Calendar Access", + title: "Access to your GitHub repositories", description: - "We need access to your google Calendar in order to call this tool...", + "We need access to your GitHub repositories in order to call this tool...", action: { label: "Grant" }, }} /> From b7641d4ff6fa28c9d0935b2ecb1fb240d5c8fc8f Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 15:01:28 -0500 Subject: [PATCH 5/9] fix: update connection options snippets --- .../snippets/how-tos/github/prereqs.jsx | 5 ++- .../how-tos/google-calendar/prereqs.jsx | 5 ++- .../snippets/how-tos/salesforce/prereqs.jsx | 32 ++++++++++++------- auth4genai/snippets/how-tos/slack/prereqs.jsx | 5 ++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/auth4genai/snippets/how-tos/github/prereqs.jsx b/auth4genai/snippets/how-tos/github/prereqs.jsx index edd74b9b4..403c8c2c1 100644 --- a/auth4genai/snippets/how-tos/github/prereqs.jsx +++ b/auth4genai/snippets/how-tos/github/prereqs.jsx @@ -83,9 +83,8 @@ export const GitHubPrereqs = ({ >
  • - Under the Purpose section, make sure to enable{" "} - Use for Authentication and{" "} - Use for Connected Accounts with Token Vault toggles. + Under the Purpose section, make sure to enable the{" "} + Use for Connected Accounts with Token Vault toggle.
  • Under the Permissions section, enable the Offline Access{" "} diff --git a/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx index f2f0c2e95..2dff55b3d 100644 --- a/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx +++ b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx @@ -87,9 +87,8 @@ export const GoogleCalendarPrereqs = ({ >
    • - Under the Purpose section, make sure to enable{" "} - Use for Authentication and{" "} - Use for Connected Accounts with Token Vault toggles. + Under the Purpose section, make sure to enable the{" "} + Use for Connected Accounts with Token Vault toggle.
    • Under the Permissions section, enable the Offline Access{" "} diff --git a/auth4genai/snippets/how-tos/salesforce/prereqs.jsx b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx index 19bcd4e70..95452aa0a 100644 --- a/auth4genai/snippets/how-tos/salesforce/prereqs.jsx +++ b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx @@ -4,7 +4,7 @@ export const SalesforcePrereqs = ({ }) => { const languageSteps = []; - if (lang === "js") { + if (lang === "js") { languageSteps.push( npm } - />, + /> ); } else if (lang === "python") { languageSteps.push( @@ -91,22 +91,32 @@ export const SalesforcePrereqs = ({ > Configure a Salesforce OIDC connection {" "} - in Auth0 with the following scopes + in Auth0. } >
      • - openid -
      • -
      • - api -
      • -
      • - refresh_token + Under the General section, ensure the following{" "} + Scopes: +
          +
        • + openid +
        • +
        • + api +
        • +
        • + refresh_token +
        • +
        • + offline_access +
        • +
      • - offline_access + Under the Purpose section, make sure to enable the{" "} + Use for Connected Accounts with Token Vault toggle.
      , diff --git a/auth4genai/snippets/how-tos/slack/prereqs.jsx b/auth4genai/snippets/how-tos/slack/prereqs.jsx index 3380e9b1a..b0fa47fcf 100644 --- a/auth4genai/snippets/how-tos/slack/prereqs.jsx +++ b/auth4genai/snippets/how-tos/slack/prereqs.jsx @@ -68,9 +68,8 @@ export const SlackPrereqs = ({ >
      • - Under the Purpose section, make sure to enable{" "} - Use for Authentication and{" "} - Use for Connected Accounts with Token Vault toggles. + Under the Purpose section, make sure to enable the{" "} + Use for Connected Accounts with Token Vault toggle.
      , From c4f91cddacdeb23d8eac7a33fa11463bb6e46aa6 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 16:22:56 -0500 Subject: [PATCH 6/9] feat: add short blurb about CloudflareKVStore --- .../snippets/common/configure-cloudflare-kv-store.mdx | 10 ++++++++++ .../get-started/cloudflare-agents-js/call-your-api.mdx | 9 +++++---- .../snippets/how-tos/github/cloudflare-agents.mdx | 6 ++++++ .../how-tos/google-calendar/cloudflare-agents.mdx | 6 ++++++ .../snippets/how-tos/slack/cloudflare-agents.mdx | 6 ++++++ 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 auth4genai/snippets/common/configure-cloudflare-kv-store.mdx diff --git a/auth4genai/snippets/common/configure-cloudflare-kv-store.mdx b/auth4genai/snippets/common/configure-cloudflare-kv-store.mdx new file mode 100644 index 000000000..b10bae9e2 --- /dev/null +++ b/auth4genai/snippets/common/configure-cloudflare-kv-store.mdx @@ -0,0 +1,10 @@ +For persisting Auth0 session data and other key-value pairs, you'll want to configure a persistent store with your Cloudflare agent worker. One easy methodology is using Workers KV and a [KV namespace](https://developers.cloudflare.com/kv/get-started/) with your Cloudflare agent workers. This will enable you to store Auth0 session data and other key-value pairs with easy access from your Cloudflare agent workers. This can be used when constructing the `CloudflareKVStore` instance with your Cloudflare agent worker. + +```ts +import { CloudflareKVStore } from '@auth0/ai-cloudflare'; +... + +return new CloudflareKVStore({ kv: this.env.YOUR_KV_NAMESPACE }); +``` + +**Note:** the `kv` prop accepts any store which implements the [KVNamespace interface](https://github.com/auth0/auth0-ai-js/blob/%40auth0/ai-cloudflare-v2.0.0/packages/ai-cloudflare/src/CloudflareKVStore.ts#L3-L14), so any persistent store which implements this interface will work. diff --git a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx index 53c7b3d53..abfd06001 100644 --- a/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx +++ b/auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx @@ -1,5 +1,6 @@ import { Prerequisites } from "/snippets/get-started/prerequisites/call-your-api.jsx"; import { AccountAndAppSteps } from "/snippets/get-started/prerequisites/account-app-steps.jsx"; +import ConfigureCloudflareKvStore from "/snippets/common/configure-cloudflare-kv-store.mdx"; @@ -47,10 +48,6 @@ In the root directory of your project, copy the `.dev.vars.example` into `.dev.v # You can use any provider of your choice supported by Vercel AI OPENAI_API_KEY="OPENAI API KEY" -SESSION_STORE=cloudflare-kv -SESSION_STORE_NAMESPACE=Session - - #auth0 AUTH0_DOMAIN="YOUR-ACCOUNT.us.auth0.com" AUTH0_CLIENT_ID="YOUR CLIENT ID" @@ -63,6 +60,10 @@ BASE_URL="http://localhost:3000" If you use another provider for your LLM, adjust the variable name in `.dev.vars` accordingly. +#### Configure a persistent store + + + ### Define a tool to call your API In this step, you'll create a Vercel AI tool to make the first-party API call to the Auth0 API. You will do the same for third-party APIs. diff --git a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx index 486fffc80..6fe3c8d86 100644 --- a/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/github/cloudflare-agents.mdx @@ -1,3 +1,5 @@ +import ConfigureCloudflareKvStore from "/snippets/common/configure-cloudflare-kv-store.mdx"; + ### 1. Configure Auth0 AI @@ -228,6 +230,10 @@ The name of the user is ${claims?.name ?? "unknown"}.`, } ``` +**Note about CloudflareKVStore:** + + + #### Client Side In this example, we utilize the `TokenVaultConsentPopup` component to show a pop-up that allows the user to authenticate with GitHub and grant access with the requested scopes. You'll first need to install the `@auth0/ai-components` package: diff --git a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx index bfcd5bbe8..271ce972a 100644 --- a/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/google-calendar/cloudflare-agents.mdx @@ -1,3 +1,5 @@ +import ConfigureCloudflareKvStore from "/snippets/common/configure-cloudflare-kv-store.mdx"; + ### 1. Configure Auth0 AI @@ -248,6 +250,10 @@ The name of the user is ${claims?.name ?? "unknown"}.`, } ``` +**Note about CloudflareKVStore:** + + + #### Client Side In this example, we utilize the `TokenVaultConsentPopup` component to show a pop-up that allows the user to authenticate with Google Calendar and grant access with the requested scopes. You'll first need to install the `@auth0/ai-components` package: diff --git a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx index c16085217..2ca00f8fb 100644 --- a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx @@ -1,3 +1,5 @@ +import ConfigureCloudflareKvStore from "/snippets/common/configure-cloudflare-kv-store.mdx"; + ### 1. Configure Auth0 AI If you started from the [Auth0 Cloudflare Agents starter kit](https://github.com/auth0-lab/cloudflare-agents-starter), you can skip this step as the Auth0 AI SDK is already configured. @@ -228,6 +230,10 @@ The name of the user is ${claims?.name ?? "unknown"}.`, } ``` +**Note about CloudflareKVStore:** + + + #### Client Side In this example, we utilize the `TokenVaultConsentPopup` component to show a pop-up that allows the user to authenticate with GitHub and grant access with the requested scopes. You'll first need to install the `@auth0/ai-components` package: From dc40d325ba38d0dee049746f0a516fb050b663ba Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 16:32:16 -0500 Subject: [PATCH 7/9] fix: removed any cast --- auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx index 2ca00f8fb..3db5cd91d 100644 --- a/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx +++ b/auth4genai/snippets/how-tos/slack/cloudflare-agents.mdx @@ -225,7 +225,7 @@ The name of the user is ${claims?.name ?? "unknown"}.`, } get auth0AIStore() { - return new CloudflareKVStore({ kv: (this as any).env.Session }); + return new CloudflareKVStore({ kv: this.env.Session }); } } ``` From f8cf45030ceff1b7475981f84e47984141fefe86 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 16:43:49 -0500 Subject: [PATCH 8/9] fix: lychee ignore --- lychee.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lychee.toml b/lychee.toml index b353fdaf6..848217ef8 100644 --- a/lychee.toml +++ b/lychee.toml @@ -205,6 +205,9 @@ exclude = [ # Constant Contact KB – currently failing TLS / blocked to bots "^https?://knowledgebase\\.constantcontact\\.com/", + # Sample Salesforce instance URL + "^https://your\\-instance\\.salesforce\\.com/", + # # Known broken or legacy URLs (exact-match) # From ab108721dd753677661acf693811ce28ea6ab3d6 Mon Sep 17 00:00:00 2001 From: "Patrick M. Riley" Date: Mon, 1 Dec 2025 18:51:55 -0500 Subject: [PATCH 9/9] fix: move custom api prereq content to common --- .../check-google-calendar-availability.mdx | 1 + .../how-tos/get-salesforce-opportunities.mdx | 1 + .../how-tos/list-github-repositories.mdx | 1 + auth4genai/how-tos/list-slack-channels.mdx | 1 + .../snippets/common/custom-api-client.jsx | 43 +++++++++++++++++++ .../snippets/how-tos/github/prereqs.jsx | 39 +---------------- .../how-tos/google-calendar/prereqs.jsx | 39 +---------------- .../snippets/how-tos/salesforce/prereqs.jsx | 39 +---------------- auth4genai/snippets/how-tos/slack/prereqs.jsx | 39 +---------------- 9 files changed, 55 insertions(+), 148 deletions(-) create mode 100644 auth4genai/snippets/common/custom-api-client.jsx diff --git a/auth4genai/how-tos/check-google-calendar-availability.mdx b/auth4genai/how-tos/check-google-calendar-availability.mdx index c52570599..0af9d2103 100644 --- a/auth4genai/how-tos/check-google-calendar-availability.mdx +++ b/auth4genai/how-tos/check-google-calendar-availability.mdx @@ -14,6 +14,7 @@ import NextJSAuth0Sample from "/snippets/how-tos/google-calendar/nextjs-auth0.md import GenKitSample from "/snippets/how-tos/google-calendar/genkit.mdx"; import { GoogleCalendarPrereqs } from "/snippets/how-tos/google-calendar/prereqs.jsx"; +import { CustomApiClient } from "/snippets/common/custom-api-client.jsx"; diff --git a/auth4genai/how-tos/get-salesforce-opportunities.mdx b/auth4genai/how-tos/get-salesforce-opportunities.mdx index 2f0edeb7b..f5665f827 100644 --- a/auth4genai/how-tos/get-salesforce-opportunities.mdx +++ b/auth4genai/how-tos/get-salesforce-opportunities.mdx @@ -5,6 +5,7 @@ mode: "wide" --- import { SalesforcePrereqs } from "/snippets/how-tos/salesforce/prereqs.jsx"; +import { CustomApiClient } from "/snippets/common/custom-api-client.jsx"; diff --git a/auth4genai/how-tos/list-github-repositories.mdx b/auth4genai/how-tos/list-github-repositories.mdx index f94ea12fa..95115899f 100644 --- a/auth4genai/how-tos/list-github-repositories.mdx +++ b/auth4genai/how-tos/list-github-repositories.mdx @@ -15,6 +15,7 @@ import LlamaIndexJSSample from "/snippets/how-tos/github/llamaindex.mdx"; import AccountLinking from "/snippets/how-tos/account-linking.mdx"; import { GitHubPrereqs } from "/snippets/how-tos/github/prereqs.jsx"; +import { CustomApiClient } from "/snippets/common/custom-api-client.jsx"; diff --git a/auth4genai/how-tos/list-slack-channels.mdx b/auth4genai/how-tos/list-slack-channels.mdx index 072ae2435..50b748f1d 100644 --- a/auth4genai/how-tos/list-slack-channels.mdx +++ b/auth4genai/how-tos/list-slack-channels.mdx @@ -13,6 +13,7 @@ import GenKitSample from "/snippets/how-tos/slack/genkit.mdx"; import LlamaIndexJSSample from "/snippets/how-tos/slack/llamaindex.mdx"; import { SlackPrereqs } from "/snippets/how-tos/slack/prereqs.jsx"; +import { CustomApiClient } from "/snippets/common/custom-api-client.jsx"; diff --git a/auth4genai/snippets/common/custom-api-client.jsx b/auth4genai/snippets/common/custom-api-client.jsx new file mode 100644 index 000000000..88b5958b4 --- /dev/null +++ b/auth4genai/snippets/common/custom-api-client.jsx @@ -0,0 +1,43 @@ +export const CustomApiClient = ({ + apiName = "the external API" +}) => { + return ( + + The Custom API Client allows your API server to perform token exchanges + using{" "} + + access tokens + {" "} + instead of{" "} + + refresh tokens + + . This client enables Token Vault to exchange an access token for an + external API access token (e.g., {apiName}). +
      +
        +
      • + Navigate to Applications > APIs +
      • +
      • + Click the Create API button to create a new Custom + API. +
      • +
      • + Go to the Custom API you created and click the{" "} + Add Application button in the right top corner. +
      • +
      • + After that click the Configure Application button + in the right top corner. +
      • +
      • + Note down the client id and client secret{" "} + for your environment variables. +
      • +
      +
      + ); +}; + + diff --git a/auth4genai/snippets/how-tos/github/prereqs.jsx b/auth4genai/snippets/how-tos/github/prereqs.jsx index 403c8c2c1..702303d3e 100644 --- a/auth4genai/snippets/how-tos/github/prereqs.jsx +++ b/auth4genai/snippets/how-tos/github/prereqs.jsx @@ -95,43 +95,8 @@ export const GitHubPrereqs = ({ ]; if (createCustomApiClientStep) { - commonSteps.push( - - The Custom API Client allows your API server to perform token exchanges - using{" "} - - access tokens - {" "} - instead of{" "} - - refresh tokens - - . This client enables Token Vault to exchange an access token for an - external API access token (e.g., GitHub API). -
      -
        -
      • - Navigate to Applications > APIs -
      • -
      • - Click the Create API button to create a new Custom - API. -
      • -
      • - Go to the Custom API you created and click the{" "} - Add Application button in the right top corner. -
      • -
      • - After that click the Configure Application button - in the right top corner. -
      • -
      • - Note down the client id and client secret{" "} - for your environment variables. -
      • -
      -
      - ); + const step = CustomApiClient({ apiName: "Github API" }); + commonSteps.push(step); } return ( diff --git a/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx index 2dff55b3d..2175ad8be 100644 --- a/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx +++ b/auth4genai/snippets/how-tos/google-calendar/prereqs.jsx @@ -99,43 +99,8 @@ export const GoogleCalendarPrereqs = ({ ]; if (createCustomApiClientStep) { - commonSteps.push( - - The Custom API Client allows your API server to perform token exchanges - using{" "} - - access tokens - {" "} - instead of{" "} - - refresh tokens - - . This client enables Token Vault to exchange an access token for an - external API access token (e.g., Google Calendar API). -
      -
        -
      • - Navigate to Applications > APIs -
      • -
      • - Click the Create API button to create a new Custom - API. -
      • -
      • - Go to the Custom API you created and click the{" "} - Add Application button in the right top corner. -
      • -
      • - After that click the Configure Application button - in the right top corner. -
      • -
      • - Note down the client id and client secret{" "} - for your environment variables. -
      • -
      -
      - ); + const step = CustomApiClient({ apiName: "Google Calendar API" }); + commonSteps.push(step); } return ( diff --git a/auth4genai/snippets/how-tos/salesforce/prereqs.jsx b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx index 95452aa0a..8fabd008e 100644 --- a/auth4genai/snippets/how-tos/salesforce/prereqs.jsx +++ b/auth4genai/snippets/how-tos/salesforce/prereqs.jsx @@ -123,43 +123,8 @@ export const SalesforcePrereqs = ({ ]; if (createCustomApiClientStep) { - commonSteps.push( - - The Custom API Client allows your API server to perform token exchanges - using{" "} - - access tokens - {" "} - instead of{" "} - - refresh tokens - - . This client enables Token Vault to exchange an access token for an - external API access token (e.g., Salesforce API). -
      -
        -
      • - Navigate to Applications > APIs -
      • -
      • - Click the Create API button to create a new Custom - API. -
      • -
      • - Go to the Custom API you created and click the{" "} - Add Application button in the right top corner. -
      • -
      • - After that click the Configure Application button - in the right top corner. -
      • -
      • - Note down the client id and client secret{" "} - for your environment variables. -
      • -
      -
      - ); + const step = CustomApiClient({ apiName: "Salesforce API" }); + commonSteps.push(step); } return ( diff --git a/auth4genai/snippets/how-tos/slack/prereqs.jsx b/auth4genai/snippets/how-tos/slack/prereqs.jsx index b0fa47fcf..063aeead3 100644 --- a/auth4genai/snippets/how-tos/slack/prereqs.jsx +++ b/auth4genai/snippets/how-tos/slack/prereqs.jsx @@ -76,43 +76,8 @@ export const SlackPrereqs = ({ ]; if (createCustomApiClientStep) { - commonSteps.push( - - The Custom API Client allows your API server to perform token exchanges - using{" "} - - access tokens - {" "} - instead of{" "} - - refresh tokens - - . This client enables Token Vault to exchange an access token for an - external API access token (e.g., Slack API). -
      -
        -
      • - Navigate to Applications > APIs -
      • -
      • - Click the Create API button to create a new Custom - API. -
      • -
      • - Go to the Custom API you created and click the{" "} - Add Application button in the right top corner. -
      • -
      • - After that click the Configure Application button - in the right top corner. -
      • -
      • - Note down the client id and client secret{" "} - for your environment variables. -
      • -
      -
      - ); + const step = CustomApiClient({ apiName: "Slack API" }); + commonSteps.push(step); } return (