> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# List GitHub Repositories

> Use OpenAI, NextJS, and the Auth0-AI SDKs to list your GitHub repositories

export const CustomApiClient = ({apiName = "the external API"}) => {
  return <Step title="Create a Custom API Client in Auth0">
      The Custom API Client allows your API server to perform token exchanges
      using{" "}
      <strong>
        <i>access tokens</i>
      </strong>{" "}
      instead of{" "}
      <strong>
        <i>refresh tokens</i>
      </strong>
      . This client enables Token Vault to exchange an access token for an
      external API access token (e.g., {apiName}).
      <br />
      <ul>
        <li>
          Navigate to <strong>Applications &gt; APIs</strong>
        </li>
        <li>
          Click the <strong>Create API</strong> button to create a new Custom
          API.
        </li>
        <li>
          Go to the Custom API you created and click the{" "}
          <strong>Add Application</strong> button in the right top corner.
        </li>
        <li>
          Once you've added the API as an application, click the <strong>Configure Application</strong> button
          in the right top corner.
        </li>
        <li>
          Note down the <code>client id</code> and <code>client secret</code>{" "}
          for your environment variables.
        </li>
      </ul>
    </Step>;
};

export const GitHubPrereqs = ({lang, createCustomApiClientStep = false}) => {
  const languageSteps = [];
  if (lang === "js") {
    languageSteps.push(<Step title={<>
            Install Node.js 20+ and <code>npm</code>
          </>} />);
  } else if (lang === "python") {
    languageSteps.push(<Step title={<>
            Install Python 3.11+ and <code>pip</code>
          </>} />);
  }
  const commonSteps = [<Step title={<>
          Complete the{" "}
          <a href="/ai/docs/get-started/user-authentication">
            User authentication quickstart
          </a>{" "}
          to create an application integrated with Auth0.
        </>} />, <Step title={<>
          <a href="https://platform.openai.com/docs/quickstart?api-mode=chat" target="_blank" rel="noopener noreferrer">
            Set up an OpenAI API key
          </a>
          .
        </>} />, <Step title={<>
          Create and configure a{" "}
          <a href="https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps" target="_blank" rel="noopener noreferrer">
            GitHub App
          </a>
          .
        </>} />, <Step title={<>
          Configure a{" "}
          <a href="https://marketplace.auth0.com/integrations/github-social-connection" target="_blank" rel="noopener noreferrer">
            Social Connection for GitHub in Auth0
          </a>
        </>}>
      <ul>
        <li>
          Under the <strong>Purpose</strong> section, make sure to enable the{" "}
          <code>Use for Connected Accounts with Token Vault</code> toggle.
        </li>
        <li>
          Under the <strong>Permissions</strong> section, enable the <code>Offline Access</code>{" "}
          scope.
        </li>
      </ul>
    </Step>];
  if (createCustomApiClientStep) {
    const step = CustomApiClient({
      apiName: "Github API"
    });
    commonSteps.push(step);
  }
  return <>
      <Heading level={3} id="prerequisites">
        Prerequisites
      </Heading>
      Before getting started, make sure you have completed the following steps:
      <Steps>{[...languageSteps, ...commonSteps]}</Steps>
    </>;
};

export const AccountLinking = ({connectionLabel = "any Identity Provider"}) => <>
    <h2>Account Linking</h2>
    <p>
      If you're integrating with {connectionLabel} and want users to be able to sign in using a different
      method (e.g., username and password or another external provider), you need to link
      these identities into a single user account. Auth0 refers to this process as{" "}
      <a href="https://auth0.com/docs/manage-users/user-accounts/user-account-linking">
        Account Linking
      </a>
      .
    </p>
    <p>
      Account Linking is separate from{" "}
      <a href="https://auth0.com/docs/secure/tokens/token-vault/connected-accounts-for-token-vault">
        Connected Accounts for Token Vault
      </a>
      :
    </p>
    <ul>
      <li>Account Linking merges multiple Auth0 identities into one user profile for sign-in.</li>
      <li>
        Connected Accounts for Token Vault allows that signed-in user to authorize an external
        provider so Auth0 can store the provider's tokens in Token Vault.
      </li>
    </ul>
    <p>
      If your application uses{" "}
      <a href="https://auth0.com/docs/manage-users/organizations">Organizations</a>, authenticate the
      user with the target organization before initiating the Connected Accounts flow. Organizations
      define the session context; they do not create a shared external account for the organization.
    </p>
    <p>
      Account Linking logic and handling varies depending on your app or agent.
      You can find an example of how to implement it in a{" "} <a href="https://github.com/auth0-lab/market0/blob/main/app/api/auth/%5Bauth0%5D/route.ts#L43">Next.js chatbot app</a>
      . If you have questions or are looking for best practices,{" "}
      <a href="http://discord.gg/XbQpZSF2Ys">join our Discord</a> and ask in the{" "}
      <code>#auth0-for-gen-ai</code> channel.
    </p>
  </>;

<Tabs>
  <Tab title="JavaScript" icon="js">
    <Tabs>
      <Tab title="AI SDK" icon="https://mintlify-assets.b-cdn.net/auth0/vercel.svg">
        <GitHubPrereqs lang="js" />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash lines theme={null}
        npm install @auth0/ai-vercel
        ```

        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 theme={null}
        import { Auth0AI } from "@auth0/ai-vercel";
        import { auth0 } from "@/lib/auth0";

        const auth0AI = new Auth0AI();

        export const withGitHub = auth0AI.withTokenVault({
          connection: "github",
          scopes: ["repo"],
          refreshToken: async () => {
            const session = await auth0.getSession();
            const refreshToken = session?.tokenSet.refreshToken as string;

            return refreshToken;
          },
        });
        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```typescript ./src/lib/tools/listRepositories.ts wrap lines highlight={2-4,9,15,19-21,31-33} theme={null}
        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";


        export const listRepositories = withGitHub(
          tool({
            description: "List respositories for the current user on GitHub",
            parameters: z.object({}),
            execute: async () => {
              // Get the access token from Auth0 AI
              const accessToken = getAccessTokenFromTokenVault();

              // GitHub SDK
              try {
                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`
                    );
                  }
                }

                throw error;
              }
            },
          })
        );
        ```

        ### 3. Handle authentication redirects

        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.

        #### Server Side

        On the server-side code of your Next.js App, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. The `setAIContext` function is used to set the async-context for the Auth0 AI SDK.

        ```typescript ./src/app/api/chat/route.ts wrap lines highlight={3-4,10,13,27,29} theme={null}
        import { createDataStreamResponse, Message, streamText } from "ai";
        import { listRepositories } from "@/lib/tools/";
        import { setAIContext } from "@auth0/ai-vercel";
        import { errorSerializer, withInterruptions } from "@auth0/ai-vercel/interrupts";
        import { openai } from "@ai-sdk/openai";

        export async function POST(request: Request) {
          const { id, messages} = await request.json();
          const tools = { listRepositories };
          setAIContext({ threadID: id });

          return createDataStreamResponse({
            execute: withInterruptions(
              async (dataStream) => {
                const result = streamText({
                  model: openai("gpt-4o-mini"),
                  system: "You are a friendly assistant! Keep your responses concise and helpful.",
                  messages,
                  maxSteps: 5,
                  tools,
                });

                result.mergeIntoDataStream(dataStream, {
                  sendReasoning: true,
                });
              },
              { messages, tools }
            ),
            onError: errorSerializer((err) => {
              console.log(err);
              return "Oops, an error occured!";
            }),
          });
        }
        ```

        #### Client Side

        On this example we utilize the `TokenVaultConsentPopup` component to show a popup 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:

        ```bash wrap lines theme={null}
        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,10-14,25-34} theme={null}
        "use client";

        import { useChat } from "@ai-sdk/react";
        import { useInterruptions } from "@auth0/ai-vercel/react";
        import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
        import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup";

        export default function Chat() {
          const { messages, handleSubmit, input, setInput, toolInterrupt } =
            useInterruptions((handler) =>
              useChat({
                onError: handler((error) => console.error("Chat error:", error)),
              })
            );

          return (
            <div>
              {messages.map((message) => (
                <div key={message.id}>
                  {message.role === "user" ? "User: " : "AI: "}
                  {message.content}
                </div>
              ))}

              {TokenVaultInterrupt.isInterrupt(toolInterrupt) && (
                <TokenVaultConsentPopup
                  interrupt={toolInterrupt}
                  connectWidget={{
                    title: "List GitHub respositories",
                    description:"description ...",
                    action: { label: "Check" },
                  }}
                />
              )}

              <form onSubmit={handleSubmit}>
                <input value={input} placeholder="Say something..." onChange={(e) => setInput(e.target.value)} />
              </form>
            </div>
          );
        }
        ```
      </Tab>

      <Tab title="Cloudflare Agents" icon="https://mintlify-assets.b-cdn.net/auth0/cloudflare.svg">
        <GitHubPrereqs lang="js" />

        ### 1. Configure Auth0 AI

        <Note>
          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.
        </Note>

        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 theme={null}
        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/agent/auth0-ai.ts wrap lines theme={null}
        import { Auth0AI, setGlobalAIContext } from "@auth0/ai-vercel";
        import { getCurrentAgent } from "agents";

        setGlobalAIContext(() => ({ threadID: getAgent().name }));

        const auth0AI = new Auth0AI({
          store: () => {
            return (getAgent() as any).auth0AIStore;
          },
        });

        const getAgent = () => {
          const { agent } = getCurrentAgent();
          if (!agent) {
            throw new Error("No agent found");
          }
          return agent;
        };

        const refreshToken = async () => {
          const credentials = getAgent().getCredentials();
          return credentials?.refresh_token;
        };

        export const withGitHub = auth0AI.withTokenVault({
          refreshToken,
          connection: "github",
          scopes: ["repo"],
        });
        ```

        ### 2. Integrate your tool with the GitHub API

        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={7-8,10,16,20-22,29-31} theme={null}
        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(
          tool({
            description: "List respositories for the current user on GitHub",
            inputSchema: z.object({}),
            execute: async () => {
              // Get the access token from Auth0 AI
              const accessToken = getAccessTokenFromTokenVault();

              // GitHub SDK
              try {
                const octokit = new Octokit({
                  auth: accessToken,
                });
                const { data } = await octokit.rest.repos.listForAuthenticatedUser();

                return data.map((repo) => repo.name);
              } catch (error) {
                if (error instanceof RequestError) {
                  if (error.status === 401) {
                    throw new TokenVaultError(
                      `Authorization required to access the Token Vault`
                    );
                  }
                }

                throw error;
              }
            },
          })
        );

        ```

        ### 3. Handle authentication redirects

        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.

        <Note>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.</Note>

        #### 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={4,6,46,70-84,95} theme={null}
        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 model = openai("gpt-4o-2024-11-20");

        const SuperAgent = extend(AIChatAgent<Env>)
          .with(AuthAgent)
          .with(OwnedAgent)
          .with(AsyncUserConfirmationResumer)
          .build();

        export class Chat extends SuperAgent {
          messages: UIMessage[] = [];

          async onChatMessage() {
            const allTools = {
              ...tools,
              ...(this.mcp?.getAITools?.() ?? {}),
            };

            const claims = this.getClaims?.();

            const stream = createUIMessageStream({
              originalMessages: this.messages,
              execute: withInterruptions(
                async ({ writer }) => {
                  await invokeTools({
                    messages: await 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: await 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"}.`,
                    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 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 });
          }
        }
        ```

        **Note about CloudflareKVStore:**

        For persisting Auth0 session data and other key-value pairs, you need to configure a persistent store with your Cloudflare agent worker. When constructing the `CloudflareKVStore` instance with your Cloudflare agent worker, you can use Workers KV and a [KV namespace](https://developers.cloudflare.com/kv/get-started/) as the persistent store. This enables you to store Auth0 session data and other key-value pairs with easy access from your Cloudflare agent workers.

        ```ts theme={null}
        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.

        #### 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:

        ```bash wrap lines theme={null}
        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/client/app.tsx wrap lines highlight={4-6,14-17,24,48-71} theme={null}
        "use client";

        import { useChat } from "@ai-sdk/react";
        import { useAgentChatInterruptions } from "@auth0/ai-cloudflare/react";
        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,
            sendMessage: handleAgentSubmit,
            addToolResult,
            clearHistory,
            toolInterrupt,
          } = chat;

          return (
            <Layout>
              {agentMessages.map((m: UIMessage, index) => {
                const isUser = m.role === "user";

                return (
                  <div key={`${m.id}-${index}`}>
                    {showDebug && (
                      <pre className="text-xs text-muted-foreground overflow-scroll">
                        {JSON.stringify(m, null, 2)}
                      </pre>
                    )}
                    <div className={`flex ${isUser ? "justify-end" : "justify-start"}`}>
                      <div
                        className={`flex gap-2 max-w-[85%] ${
                          isUser ? "flex-row-reverse" : "flex-row"
                        }`}
                      >
                        <div>
                          <div>
                            {m.parts?.map((part: any, i) => {
                              if (
                                part?.type?.startsWith("tool-") &&
                                toolInterrupt &&
                                TokenVaultInterrupt.isInterrupt(toolInterrupt)
                              ) {
                                return (
                                  <TokenVaultConsentPopup
                                    key={toolInterrupt?.toolCall?.id}
                                    interrupt={toolInterrupt}
                                    auth={{ authorizePath: "/auth/login" }}
                                    connectWidget={{
                                      title: "Access to your GitHub repositories",
                                      description:
                                        "We need access to your GitHub repositories in order to call this tool...",
                                      action: { label: "Grant" },
                                    }}
                                  />
                                );
                              }
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                );
              })}

              <form onSubmit={handleSubmit}>
                <input
                  value={input}
                  placeholder="Say something..."
                  onChange={(e) => setInput(e.target.value)}
                />
              </form>
            </Layout>
          );
        }
        ```
      </Tab>

      <Tab title="LangGraph" icon="https://mintlify-assets.b-cdn.net/auth0/langchain.svg">
        <GitHubPrereqs lang="js" createCustomApiClientStep={true} />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash wrap lines theme={null}
        npm install @auth0/ai-langchain
        ```

        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 theme={null}
        import { SUBJECT_TOKEN_TYPES } from "@auth0/ai";
        import { Auth0AI } from "@auth0/ai-langchain";

        const auth0AI = new Auth0AI({
          auth0: {
            domain: process.env.AUTH0_DOMAIN!,
            clientId: process.env.AUTH0_CUSTOM_API_CLIENT_ID!,
            clientSecret: process.env.AUTH0_CUSTOM_API_CLIENT_SECRET!,
          },
        });

        const withAccessTokenForConnection = (connection: string, scopes: string[]) =>
          auth0AI.withTokenVault({
            connection,
            scopes,
            accessToken: async (_, config) => {
              return config.configurable?.langgraph_auth_user?.getRawAccessToken();
            },
            subjectTokenType: SUBJECT_TOKEN_TYPES.SUBJECT_TYPE_ACCESS_TOKEN,
          });

        export const withGitHub = withAccessTokenForConnection("github", ["repo"]);
        ```

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```typescript ./src/lib/tools/listRepositories.ts wrap lines highlight={3-5,9,12,16-18,28-30} theme={null}
        import { Octokit } from "@octokit/rest";
        import { RequestError } from "@octokit/request-error";
        import { getAccessTokenFromTokenVault } from "@auth0/ai-langchain";
        import { TokenVaultError } from "@auth0/ai/interrupts";
        import { withGitHub } from "@/lib/auth0-ai";
        import { tool } from "@langchain/core/tools";
        import { z } from "zod";

        export const listRepositories = withGitHub(
          tool(async () => {
            // Get the access token from Auth0 AI
            const accessToken = getAccessTokenFromTokenVault();

            // GitHub SDK
            try {
              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`
                  );
                }
              }

              throw error;
            }
          },
          {
            name: "list_github_repositories",
            description: "List respositories for the current user on GitHub",
            schema: z.object({}),
          })
        );
        ```

        Now that the tool is protected, you can pass it your LangGraph agent as part of a `ToolNode`. The agent will automatically request the access token when the tool is called.

        ```typescript src/lib/agent.ts wrap lines highlight={7,10,33,39} theme={null}
        import { AIMessage } from "@langchain/core/messages";
        import { RunnableLike } from "@langchain/core/runnables";
        import { END, InMemoryStore, MemorySaver, MessagesAnnotation, START, StateGraph } from "@langchain/langgraph";
        import { ToolNode } from "@langchain/langgraph/prebuilt";
        import { ChatOpenAI } from "@langchain/openai";

        import { listRepositories } from "@/lib/tools/listRepositories";

        const model = new ChatOpenAI({ model: "gpt-4o", }).bindTools([
          listRepositories,
        ]);

        const callLLM = async (state: typeof MessagesAnnotation.State) => {
          const response = await model.invoke(state.messages);
          return { messages: [response] };
        };

        const routeAfterLLM: RunnableLike = function (state) {
          const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
          if (!lastMessage.tool_calls?.length) {
            return END;
          }
          return "tools";
        };

        const stateGraph = new StateGraph(MessagesAnnotation)
          .addNode("callLLM", callLLM)
          .addNode(
            "tools",
            new ToolNode(
              [
                // A tool with Token Vault access
                listRepositories,
                // ... other tools
              ],
              {
                // Error handler should be disabled in order to
                // trigger interruptions from within tools.
                handleToolErrors: false,
              }
            )
          )
          .addEdge(START, "callLLM")
          .addConditionalEdges("callLLM", routeAfterLLM, [END, "tools"])
          .addEdge("tools", "callLLM");

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

        export const graph = stateGraph.compile({
          checkpointer,
          store,
          interruptBefore: [],
          interruptAfter: [],
        });
        ```

        ### 3. Handle authentication redirects

        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 such authentication redirects integrated with the Langchain SDK.

        #### Server Side

        On the server side of your Next.js application you need to set up a route to handle the Chat API requests. This route will be responsible for forwarding the requests to the LangGraph API. Additionally, you must provide the `accessToken` in the headers.

        ```typescript ./src/app/api/langgraph/[..._path]/route.ts wrap lines theme={null}
        import { initApiPassthrough } from "langgraph-nextjs-api-passthrough";
        import { NextRequest } from "next/server";

        import { auth0 } from "@/lib/auth0";

        async function getAccessToken() {
          const tokenResult = await auth0.getAccessToken();
          if (!tokenResult?.token) {
            throw new Error("Error retrieving access token for langgraph api.");
          }
          return tokenResult.token;
        }

        export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, runtime } =
          initApiPassthrough({
            apiUrl: process.env.LANGGRAPH_API_URL,
            apiKey: process.env.LANGSMITH_API_KEY,
            runtime: "edge",
            baseRoute: "langgraph/",
            headers: async (req: NextRequest) => {
              const headers: Record<string, string> = {};
              req.headers.forEach((value, key) => {
                headers[key] = value;
              });

              const accessToken = await getAccessToken();
              headers["Authorization"] = `Bearer ${accessToken}`;
              return headers;
            },
          });


        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        ### Add Custom Authentication

        <Info>
          For more information on how to add custom authentication for your LangGraph Platform application, see the [Custom Auth](https://langchain-ai.github.io/langgraphjs/how-tos/auth/custom_auth/) guide.
        </Info>

        In your langgraph.json, add the path to your auth file:

        ```typescript langgraph.json wrap lines highlight={8} theme={null}
        {
          "node_version": "20",
          "graphs": {
            "agent": "./src/lib/agent.ts:agent"
          },
          "env": ".env",
          "auth": {
            "path": "./src/lib/auth.ts:authHandler"
          }
        }
        ```

        Then, in your auth.ts file, add your auth logic:

        ```typescript src/lib/auth.ts wrap lines theme={null}
        import { createRemoteJWKSet, jwtVerify } from "jose";

        const { Auth, HTTPException } = require("@langchain/langgraph-sdk/auth");

        const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;
        const AUTH0_AUDIENCE = process.env.AUTH0_AUDIENCE;

        // JWKS endpoint for Auth0
        const JWKS = createRemoteJWKSet(
          new URL(`https://${AUTH0_DOMAIN}/.well-known/jwks.json`)
        );

        // Create the Auth instance
        const auth = new Auth();
        // Register the authentication handler
        auth.authenticate(async (request: Request) => {
          const authHeader = request.headers.get("Authorization");
          const xApiKeyHeader = request.headers.get("x-api-key");
            /**
             * LangGraph Platform will convert the `Authorization` header from the client to an `x-api-key` header automatically
             * as of now: https://docs.langchain.com/langgraph-platform/custom-auth
             *
             * We can still leverage the `Authorization` header when served in other infrastructure w/ langgraph-cli
             * or when running locally.
             */
            // This header is required in Langgraph Cloud.
            if (!authHeader && !xApiKeyHeader) {
              throw new HTTPException(401, {
                message: "Invalid auth header provided.",
              });
            }

            // prefer the xApiKeyHeader first
            let token = xApiKeyHeader || authHeader;

            // Remove "Bearer " prefix if present
            if (token && token.startsWith("Bearer ")) {
              token = token.substring(7);
            }

            // Validate Auth0 Access Token using common JWKS endpoint
            if (!token) {
              throw new HTTPException(401, {
                message:
                  "Authorization header format must be of the form: Bearer <token>",
              });
            }

            if (token) {
              try {
                // Verify the JWT using Auth0 JWKS
                const { payload } = await jwtVerify(token, JWKS, {
                  issuer: `https://${AUTH0_DOMAIN}/`,
                  audience: AUTH0_AUDIENCE,
                });

                console.log("✅ Auth0 JWT payload resolved!", payload);

                // Return the verified payload - this becomes available in graph nodes
                return {
                  identity: payload.sub!,
                  email: payload.email as string,
                  permissions:
                    typeof payload.scope === "string" ? payload.scope.split(" ") : [],
                  auth_type: "auth0",
                  // include the access token for use with Auth0 Token Vault exchanges by tools
                  getRawAccessToken: () => token,
                  // Add any other claims you need
                  ...payload,
                };
              } catch (jwtError) {
                console.log(
                  "Auth0 JWT validation failed:",
                  jwtError instanceof Error ? jwtError.message : "Unknown error"
                );
                throw new HTTPException(401, {
                  message: "Invalid Authorization token provided.",
                });
              }
            }
        });

        export { auth as authHandler };
        ```

        #### 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:

        ```bash wrap lines theme={null}
        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={2-3,62-74} theme={null}
        import { useStream } from "@langchain/langgraph-sdk/react";
        import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
        import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup";

        const useFocus = () => {
          const htmlElRef = useRef<HTMLInputElement>(null);
          const setFocus = () => {
            if (!htmlElRef.current) {
              return;
            }
            htmlElRef.current.focus();
          };
          return [htmlElRef, setFocus] as const;
        };

        export default function Chat() {
          const [threadId, setThreadId] = useQueryState("threadId");
          const [input, setInput] = useState("");
          const thread = useStream({
            apiUrl: `${process.env.NEXT_PUBLIC_URL}/api/langgraph`,
            assistantId: "agent",
            threadId,
            onThreadId: setThreadId,
            onError: (err) => {
              console.dir(err);
            },
          });

          const [inputRef, setInputFocus] = useFocus();
          useEffect(() => {
            if (thread.isLoading) {
              return;
            }
            setInputFocus();
          }, [thread.isLoading, setInputFocus]);

          const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
            e.preventDefault();
            thread.submit(
              { messages: [{ type: "human", content: input }] },
              {
                optimisticValues: (prev) => ({
                  messages: [
                    ...((prev?.messages as []) ?? []),
                    { type: "human", content: input, id: "temp" },
                  ],
                }),
              }
            );
            setInput("");
          };

          return (
            <div>
              {thread.messages.filter((m) => m.content && ["human", "ai"].includes(m.type)).map((message) => (
                <div key={message.id}>
                  {message.type === "human" ? "User: " : "AI: "}
                  {message.content as string}
                </div>
              ))}

              {thread.interrupt && TokenVaultInterrupt.isInterrupt(thread.interrupt.value) ? (
                <div key={thread.interrupt.ns?.join("")}>
                  <TokenVaultConsentPopup
                    interrupt={thread.interrupt.value}
                    onFinish={() => thread.submit(null)}
                    connectWidget={{
                        title: "List GitHub respositories",
                        description:"description ...",
                        action: { label: "Check" },
                      }}
                  />
                </div>
              ) : null}

              <form onSubmit={handleSubmit}>
                <input ref={inputRef} value={input} placeholder="Say something..." readOnly={thread.isLoading} disabled={thread.isLoading} onChange={(e) => setInput(e.target.value)} />
              </form>
            </div>
          );
        }
        ```
      </Tab>

      <Tab title="GenKit" icon="https://mintlify-assets.b-cdn.net/auth0/genkit.svg">
        <GitHubPrereqs lang="js" />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash wrap lines theme={null}
        npm install @auth0/ai-genkit
        ```

        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 theme={null}
        import { Auth0AI } from "@auth0/ai-genkit";
        import { auth0 } from "@/lib/auth0";

        // importing GenKit instance
        import { ai } from "./genkit";

        const auth0AI = new Auth0AI({
          genkit: ai,
        });

        export const withGitHub = auth0AI.withTokenVault({
          connection: "github",
          scopes: ["repo"],
          refreshToken: async () => {
            const session = await auth0.getSession();
            const refreshToken = session?.tokenSet.refreshToken as string;
            return refreshToken;
          },
        });

        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```typescript ./src/lib/tools/listRepositories.ts wrap lines highlight={3-5,11,19,33-35} theme={null}
        import { Octokit, RequestError } from "octokit";
        import { z } from "zod";
        import { getAccessTokenFromTokenVault } from "@auth0/ai-genkit";
        import { TokenVaultError } from "@auth0/ai/interrupts";
        import { withGoogleCalendar } from "@/lib/auth0-ai";

        // importing GenKit instance
        import { ai } from "../genkit";

        export const listRepositories = ai.defineTool(
          ...withGitHub(
            {
              description: "List respositories for the current user on GitHub",
              inputSchema: z.object({}),
              name: "listRepositories",
            },
            async () => {
              // Get the access token from Auth0 AI
              const accessToken = getAccessTokenFromTokenVault();

              try {
                // GitHub SDK
                const octokit = new Octokit({
                  auth: accessToken,
                });

                const { data } = await octokit.rest.repos.listForAuthenticatedUser();

                return data.map((repo) => repo.name);
              } catch (error) {
                if (error instanceof RequestError) {
                  if (error.status === 401) {
                    throw new TokenVaultError(
                      `Authorization required to access the Token Vault connection`
                    );
                  }
                }

                throw error;
              }
            }
          )
        );
        ```

        ### 3. Handle authentication redirects

        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 GenKit SDK via these interrupts.

        #### Server Side

        On the server-side code of your Next.js App, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. The `setAIContext` function is used to set the async-context for the Auth0 AI SDK.

        ```typescript ./src/app/api/chat/route.ts wrap lines highlight={4-5,47} theme={null}
        import { ToolRequestPart } from "genkit";
        import path from "path";
        import { ai } from "@/lib/genkit";
        import { listRepositories } from "@/lib/tools/list-repositories";
        import { resumeAuth0Interrupts } from "@auth0/ai-genkit";
        import { auth0 } from "@/lib/auth0";

        export async function POST(
          request: Request,
          { params }: { params: Promise<{ id: string }> }
        ) {
          const auth0Session = await auth0.getSession();
          const { id } = await params;
          const {
            message,
            interruptedToolRequest,
            timezone,
          }: {
            message?: string;
            interruptedToolRequest?: ToolRequestPart;
            timezone: { region: string; offset: number };
          } = await request.json();

          let session = await ai.loadSession(id);

          if (!session) {
            session = ai.createSession({
              sessionId: id,
            });
          }

          const tools = [listRepositories];

          const chat = session.chat({
            tools: tools,
            system: `You are a helpful assistant.
            The user's timezone is ${timezone.region} with an offset of ${timezone.offset} minutes.
            User's details: ${JSON.stringify(auth0Session?.user, null, 2)}.
            You can use the tools provided to help the user.
            You can also ask the user for more information if needed.
            Chat started at ${new Date().toISOString()}
            `,
          });

          const r = await chat.send({
            prompt: message,
            resume: resumeAuth0Interrupts(tools, interruptedToolRequest),
          });

          return Response.json({ messages: r.messages, interrupts: r.interrupts });
        }

        export async function GET(
          request: Request,
          { params }: { params: Promise<{ id: string }> }
        ) {
          const { id } = await params;

          const session = await ai.loadSession(id);

          if (!session) {
            return new Response("Session not found", {
              status: 404,
            });
          }

          const json = session.toJSON();

          if (!json?.threads?.main) {
            return new Response("Session not found", {
              status: 404,
            });
          }

          return Response.json(json.threads.main);
        }
        ```

        #### 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:

        ```bash wrap lines theme={null}
        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-5,120-141} theme={null}
        "use client";
        import { useQueryState } from "nuqs";
        import { FormEventHandler, useEffect, useRef, useState } from "react";
        import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
        import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup";
        import Markdown from "react-markdown";

        const useFocus = () => {
          const htmlElRef = useRef<HTMLInputElement>(null);
          const setFocus = () => {
            if (!htmlElRef.current) {
              return;
            }
            htmlElRef.current.focus();
          };
          return [htmlElRef, setFocus] as const;
        };

        export default function Chat() {
          const [threadId, setThreadId] = useQueryState("threadId");
          const [input, setInput] = useState("");
          const [isLoading, setIsLoading] = useState(false);
          const [messages, setMessages] = useState<
            {
              role: "user" | "model";
              content: [{ text?: string; metadata?: { interrupt?: any } }];
            }[]
          >([]);

          useEffect(() => {
            if (!threadId) {
              setThreadId(self.crypto.randomUUID());
            }
          }, [threadId, setThreadId]);

          useEffect(() => {
            if (!threadId) {
              return;
            }

            setIsLoading(true);

            (async () => {
              const messagesResponse = await fetch(`/api/chat/${threadId}`, {
                method: "GET",
                credentials: "include",
              });
              if (!messagesResponse.ok) {
                setMessages([]);
              } else {
                setMessages(await messagesResponse.json());
              }
              setIsLoading(false);
            })();
          }, [threadId]);

          const [inputRef, setInputFocus] = useFocus();
          useEffect(() => {
            if (isLoading) {
              return;
            }
            setInputFocus();
          }, [isLoading, setInputFocus]);

          const submit = async ({
            message,
            interruptedToolRequest,
          }: {
            message?: string;
            interruptedToolRequest?: any;
          }) => {
            setIsLoading(true);
            const timezone = {
              region: Intl.DateTimeFormat().resolvedOptions().timeZone,
              offset: new Date().getTimezoneOffset(),
            };
            const response = await fetch(`/api/chat/${threadId}`, {
              method: "POST",
              credentials: "include",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ message, interruptedToolRequest, timezone }),
            });
            if (!response.ok) {
              console.error("Error sending message");
            } else {
              const { messages: messagesResponse } = await response.json();
              setMessages(messagesResponse);
            }
            setIsLoading(false);
          };

          // //When the user submits a message, add it to the list of messages and resume the conversation.
          const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
            e.preventDefault();
            setMessages((messages) => [
              ...messages,
              { role: "user", content: [{ text: input }] },
            ]);
            submit({ message: input });
            setInput("");
          };

          return (
            <div>
              {messages
                .filter(
                  (m) =>
                    ["model", "user", "tool"].includes(m.role) &&
                    m.content?.length > 0 &&
                    (m.content[0].text || m.content[0].metadata?.interrupt)
                )
                .map((message, index) => (
                  <div key={index}>
                    <Markdown>
                      {(message.role === "user" ? "User: " : "AI: ") +
                        (message.content[0].text || "")}
                    </Markdown>
                    {!isLoading &&
                    message.content[0].metadata?.interrupt &&
                    TokenVaultInterrupt.isInterrupt(
                      message.content[0].metadata?.interrupt
                    )
                      ? (() => {
                          const interrupt: any = message.content[0].metadata?.interrupt;
                          return (
                            <div>
                              <TokenVaultConsentPopup
                                onFinish={() => submit({ interruptedToolRequest: message.content[0] })}
                                interrupt={interrupt}
                                connectWidget={{
                                  title: `Requested by: "${interrupt.toolCall.toolName}"`,
                                  description: "Description...",
                                  action: { label: "Check" },
                                }}
                              />
                            </div>
                          );
                        })()
                      : null}
                  </div>
                ))}

              <form onSubmit={handleSubmit}>
                <input value={input} ref={inputRef} placeholder="Say something..." readOnly={isLoading} disabled={isLoading} onChange={(e) => setInput(e.target.value)} />
              </form>
            </div>
          );
        }
        ```
      </Tab>

      <Tab title="LlamaIndex" icon="https://mintlify-assets.b-cdn.net/auth0/llamadex.svg">
        <GitHubPrereqs lang="js" />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash wrap lines theme={null}
        npm install @auth0/ai-llamaindex
        ```

        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 theme={null}
        import { Auth0AI } from "@auth0/ai-llamaindex";
        import { auth0 } from "@/lib/auth0";

        const auth0AI = new Auth0AI();

        export const withGitHub = auth0AI.withTokenVault({
          connection: "github",
          scopes: ["repo"],
          refreshToken: async () => {
            const session = await auth0.getSession();
            const refreshToken = session?.tokenSet.refreshToken as string;
            return refreshToken;
          },
        });

        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```typescript ./src/lib/tools/listRepositories.ts wrap lines highlight={3-5,13,18,27-29} theme={null}
        import { Octokit, RequestError } from "octokit";
        import { z } from "zod";
        import { withGitHub } from "@/lib/auth0-ai";
        import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel";
        import { TokenVaultError } from "@auth0/ai/interrupts";
        import { tool } from "llamaindex";

        export const listRepositories = () =>
          withGitHub(
            tool(
              async () => {
                // Get the access token from Auth0 AI
                const accessToken = getAccessTokenFromTokenVault();

                // GitHub SDK
                try {
                  const octokit = new Octokit({
                    auth: accessToken,
                  });

                  const { data } = await octokit.rest.repos.listForAuthenticatedUser();

                  return data.map((repo) => repo.name);
                } catch (error) {
                  if (error instanceof RequestError) {
                    if (error.status === 401) {
                      throw new TokenVaultError(
                        `Authorization required to access the Token Vault connection`
                      );
                    }
                  }

                  throw error;
                }
              },
              {
                name: "listRepositories",
                description: "List respositories for the current user on GitHub",
                parameters: z.object({}),
              }
            )
          );
        ```

        ### 3. Handle authentication redirects

        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 LlamaIndex SDK via these interrupts.

        #### Server Side

        On the server-side code of your Next.js App, you need to set up the tool invocation and handle the interruption messaging via the `errorSerializer`. The `setAIContext` function is used to set the async-context for the Auth0 AI SDK.

        ```typescript ./src/app/api/chat/route.ts wrap lines highlight={2-5,15,35} theme={null}
        import { createDataStreamResponse, LlamaIndexAdapter, Message, ToolExecutionError } from "ai";
        import { listRepositories } from "@/lib/tools/";
        import { setAIContext } from "@auth0/ai-llamaindex";
        import { withInterruptions } from "@auth0/ai-llamaindex/interrupts";
        import { errorSerializer } from "@auth0/ai-vercel/interrupts";
        import { OpenAIAgent } from "llamaindex";

        export async function POST(request: Request) {
          const { id, messages }: { id: string; messages: Message[] } =
            await request.json();

          setAIContext({ threadID: id });

          return createDataStreamResponse({
            execute: withInterruptions(
              async (dataStream) => {
                const agent = new OpenAIAgent({
                  systemPrompt: "You are an AI assistant",
                  tools: [listRepositories()],
                  verbose: true,
                });

                const stream = await agent.chat({
                  message: messages[messages.length - 1].content,
                  stream: true,
                });

                LlamaIndexAdapter.mergeIntoDataStream(stream as any, { dataStream });
              },
              {
                messages,
                errorType: ToolExecutionError,
              }
            ),
            onError: errorSerializer((err) => {
              console.log(err);
              return "Oops, an error occured!";
            }),
          });
        }
        ```

        #### 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:

        ```bash wrap lines theme={null}
        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,28-38} theme={null}
        "use client";

        import { generateId } from "ai";
        import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup";
        import { useInterruptions } from "@auth0/ai-vercel/react";
        import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
        import { useChat } from "@ai-sdk/react";

        export default function Chat() {
          const { messages, handleSubmit, input, setInput, toolInterrupt } =
            useInterruptions((handler) =>
              useChat({
                experimental_throttle: 100,
                sendExtraMessageFields: true,
                generateId,
                onError: handler((error) => console.error("Chat error:", error)),
              })
            );

          return (
            <div>
              {messages.map((message) => (
                <div key={message.id}>
                  {message.role === "user" ? "User: " : "AI: "}
                  {message.content}
                  {message.parts && message.parts.length > 0 && (
                    <div>
                      {toolInterrupt?.toolCall.id.includes(message.id) &&
                        TokenVaultInterrupt.isInterrupt(toolInterrupt) && (
                          <TokenVaultConsentPopup
                            interrupt={toolInterrupt}
                            connectWidget={{
                              title: `Requested by: "${toolInterrupt.toolCall.name}"`,
                              description: "Description...",
                              action: { label: "Check" },
                            }}
                          />
                        )}
                    </div>
                  )}
                </div>
              ))}

              <form onSubmit={handleSubmit}>
                <input value={input} placeholder="Say something..." onChange={(e) => setInput(e.target.value)} autoFocus />
              </form>
            </div>
          );
        }
        ```
      </Tab>

      <Tab title="NextJS-Auth0" icon="https://mintlify-assets.b-cdn.net/auth0/nextjs-svgrepo-com.svg">
        <GitHubPrereqs lang="js" />

        ### 1. Before you start

        * Ensure that the GitHub connection in Auth0 (`github`) has the following scopes configured:
          * `repo`
          * `read:user`

        ### 2. Integrate your tool with GitHub

        ```ts ./src/lib/tools/listRepos.ts wrap lines highlight={5,11-12} theme={null}
        import { tool } from "ai";
        import { z } from 'zod';
        import { openai } from "@ai-sdk/openai";
        import { Octokit } from "@octokit/rest";
        import { auth0 } from "@/lib/auth0";

        export const listRepos = tool({
          description: 'List respositories for the current user on GitHub',
          parameters: z.object({}),
          execute: async () => {
            const { accessToken } = await auth0.getAccessTokenForConnection({ connection: "github" });
            const octokit = new Octokit({ auth: accessToken });

            const response = await octokit.request('GET /user/repos', {
              visibility: 'all',
            });

            const filteredRepos = response.data.map(repo => ({
              id: repo.id,
              full_name: repo.full_name,
              private: repo.private,
              owner_name: repo.owner.login,
              url: repo.html_url,
              description: repo.description,
              stars: repo.stargazers_count,
              forks: repo.forks_count,
            }));

            return filteredRepos;
          }
        });
        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        ### 3. Set up the API route for the chat

        Create an AI tool that fetches GitHub repositories for the authenticated user using Auth0 to get a GitHub access token:

        ```tsx ./src/app/api/chat/route.ts wrap lines highlight={6,17} theme={null}
        import { z } from 'zod';
        import { streamText, tool } from "ai"
        import { openai } from "@ai-sdk/openai"
        const { Octokit } = require("@octokit/rest");

        import { listRepos } from "@/lib/tools/listRepos";

        export const maxDuration = 60;

        export async function POST(req) {
          const { messages } = await req.json()

          const response = streamText({
            model: openai('gpt-4o'),
            messages,
            system: "You're a helpful AI agent that fetches GitHub repositories",
            tools: { listRepos }
          })
          return response.toDataStreamResponse();
        }
        ```

        ### 4. Call from the client side

        ```tsx ./src/app/page.tsx wrap lines theme={null}
        'use client';

        import { useChat } from '@ai-sdk/react';

        export default function Chat() {
          const { messages, input, handleInputChange, handleSubmit } = useChat();

          return (
            <div className="flex flex-col w-full max-w-3xl py-24 mx-auto stretch text-gray-100">
              {messages.map(message => (
                <div key={message.id} className="whitespace-pre-wrap">
                  {message.role === 'user' ? 'User: ' : 'AI: '}
                  {message.parts.map((part, i) => {
                    switch (part.type) {
                      case 'text':
                        return <div key={`${message.id}-${i}`}>{part.text}</div>;
                    }
                  })}
                </div>
              ))}

              <form onSubmit={handleSubmit}>
                <input onChange={handleInputChange} value={input} placeholder="Say something..." className="fixed bottom-0 w-full max-w-3xl p-2 mb-8 border border-zinc-300 rounded shadow-xl text-black" />
              </form>
            </div>
          );
        }
        ```

        Navigate to `https://localhost:3000` to see the chat UI show an array of returned GitHub repos for the user.
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Python" icon="python">
    <Tabs>
      <Tab title="LangGraph" icon="https://mintlify-assets.b-cdn.net/auth0/langchain.svg">
        <GitHubPrereqs lang="python" />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash wrap lines theme={null}
        pip install auth0-ai-langchain
        ```

        Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required GitHub scopes.

        ```python ./src/lib/auth0-ai.py wrap lines theme={null}
        from auth0_ai_langchain.auth0_ai import Auth0AI

        auth0_ai = Auth0AI()

        with_github = auth0_ai.with_token_vault(
            connection="github",
            scopes=["repo"]
            # Optional: By default, the SDK will expect the refresh token from
            # the LangChain RunnableConfig (`config.configurable._credentials.refresh_token`)
            # If you want to use a different store for refresh token you can set up a getter here
            # refresh_token=lambda *_args, **_kwargs:session["user"]["refresh_token"],
        )
        ```

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```python ./src/lib/tools/list_repositories.py wrap lines highlight={5-6,13,23,25} theme={null}
        from github import Github
        from github.GithubException import BadCredentialsException
        from pydantic import BaseModel
        from langchain_core.tools import StructuredTool
        from auth0_ai_langchain.token_vault import get_access_token_from_token_vault, TokenVaultError
        from src.lib.auth0_ai import with_github

        class EmptySchema(BaseModel):
            pass

        def list_repositories_tool_function(date: datetime):
            # Get the access token from Auth0 AI
            access_token = get_access_token_from_token_vault()

            # GitHub SDK
            try:
                g = Github(access_token)
                user = g.get_user()
                repos = user.get_repos()
                repo_names = [repo.name for repo in repos]
                return repo_names
            except BadCredentialsException:
                raise TokenVaultError("Authorization required to access the Token Vault API")

        list_github_repositories_tool = with_github(StructuredTool(
            name="list_github_repositories",
            description="List respositories for the current user on GitHub",
            args_schema=EmptySchema,
            func=list_repositories_tool_function,
        ))
        ```

        Now that the tool is protected, you can pass it your LangGraph agent as part of a `ToolNode`. The agent will automatically request the access token when the tool is called.

        ```python ./src/lib/agent.py wrap lines highlight={8,15,37,42} theme={null}
        from typing import Annotated, Sequence, TypedDict
        from langchain.storage import InMemoryStore
        from langchain_core.messages import AIMessage, BaseMessage
        from langchain_openai import ChatOpenAI
        from langgraph.checkpoint.memory import MemorySaver
        from langgraph.graph import END, START, StateGraph, add_messages
        from langgraph.prebuilt import ToolNode
        from tools.list_repositories import list_github_repositories_tool


        class State(TypedDict):
            messages: Annotated[Sequence[BaseMessage], add_messages]

        llm = ChatOpenAI(model="gpt-4o")
        llm.bind_tools([list_github_repositories_tool])

        async def call_llm(state: State):
            response = await llm.ainvoke(state["messages"])
            return {"messages": [response]}

        def route_after_llm(state: State):
            messages = state["messages"]
            last_message = messages[-1] if messages else None

            if isinstance(last_message, AIMessage) and last_message.tool_calls:
                return "tools"
            return END

        workflow = (
            StateGraph(State)
            .add_node("call_llm", call_llm)
            .add_node(
                "tools",
                ToolNode(
                    [
                        # a tool with Token Vault access
                        list_github_repositories_tool,
                        # ... other tools
                    ],
                    # The error handler should be disabled to
                    # allow interruptions to be triggered from within tools.
                    handle_tool_errors=False
                )
            )
            .add_edge(START, "call_llm")
            .add_edge("tools", "call_llm")
            .add_conditional_edges("call_llm", route_after_llm, [END, "tools"])
        )

        graph = workflow.compile(checkpointer=MemorySaver(), store=InMemoryStore())
        ```

        ### 3. Handle authentication redirects

        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 such authentication redirects integrated with the Langchain SDK.

        #### Server Side

        On the server side of your Next.js application you need to set up a route to handle the Chat API requests. This route will be responsible for forwarding the requests to the LangGraph API. Additionally, you must provide the `refreshToken` to the Langchain's RunnableConfig from the authenticated user's session.

        ```typescript ./src/app/api/langgraph/[..._path]/route.ts wrap lines highlight={2,23-29} theme={null}
        import { initApiPassthrough } from "langgraph-nextjs-api-passthrough";
        import { auth0 } from "@/lib/auth0";

        const getRefreshToken = async () => {
          const session = await auth0.getSession();
          const refreshToken = session?.tokenSet.refreshToken as string;
          return refreshToken;
        };

        export const { GET, POST, PUT, PATCH, DELETE, OPTIONS, runtime } =
          initApiPassthrough({
            apiUrl: process.env.LANGGRAPH_API_URL,
            apiKey: process.env.LANGSMITH_API_KEY,
            runtime: "edge",
            baseRoute: "langgraph/",
            bodyParameters: async (req, body) => {
              if (
                req.nextUrl.pathname.endsWith("/runs/stream") &&
                req.method === "POST"
              ) {
                return {
                  ...body,
                  config: {
                    configurable: {
                      _credentials: {
                        refreshToken: await getRefreshToken(),
                      },
                    },
                  },
                };
              }

              return body;
            },
          });
        ```

        <Info>
          Here, the property `auth0` is an instance of `@auth0/nextjs-auth0` to handle the application auth flows. <br />
          You can check different authentication options for Next.js with Auth0 at the [official documentation.](https://github.com/auth0/nextjs-auth0?tab=readme-ov-file#3-create-the-auth0-sdk-client)
        </Info>

        #### 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:

        ```bash wrap lines theme={null}
        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={2-3,62-74} theme={null}
        import { useStream } from "@langchain/langgraph-sdk/react";
        import { TokenVaultInterrupt } from "@auth0/ai/interrupts";
        import { TokenVaultConsentPopup } from "@/components/auth0-ai/TokenVault/popup";

        const useFocus = () => {
          const htmlElRef = useRef<HTMLInputElement>(null);
          const setFocus = () => {
            if (!htmlElRef.current) {
              return;
            }
            htmlElRef.current.focus();
          };
          return [htmlElRef, setFocus] as const;
        };

        export default function Chat() {
          const [threadId, setThreadId] = useQueryState("threadId");
          const [input, setInput] = useState("");
          const thread = useStream({
            apiUrl: `${process.env.NEXT_PUBLIC_URL}/api/langgraph`,
            assistantId: "agent",
            threadId,
            onThreadId: setThreadId,
            onError: (err) => {
              console.dir(err);
            },
          });

          const [inputRef, setInputFocus] = useFocus();
          useEffect(() => {
            if (thread.isLoading) {
              return;
            }
            setInputFocus();
          }, [thread.isLoading, setInputFocus]);

          const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
            e.preventDefault();
            thread.submit(
              { messages: [{ type: "human", content: input }] },
              {
                optimisticValues: (prev) => ({
                  messages: [
                    ...((prev?.messages as []) ?? []),
                    { type: "human", content: input, id: "temp" },
                  ],
                }),
              }
            );
            setInput("");
          };

          return (
            <div>
              {thread.messages.filter((m) => m.content && ["human", "ai"].includes(m.type)).map((message) => (
                <div key={message.id}>
                  {message.type === "human" ? "User: " : "AI: "}
                  {message.content as string}
                </div>
              ))}

              {thread.interrupt && TokenVaultInterrupt.isInterrupt(thread.interrupt.value) ? (
                <div key={thread.interrupt.ns?.join("")}>
                  <TokenVaultConsentPopup
                    interrupt={thread.interrupt.value}
                    onFinish={() => thread.submit(null)}
                    connectWidget={{
                        title: "List GitHub respositories",
                        description:"description ...",
                        action: { label: "Check" },
                      }}
                  />
                </div>
              ) : null}

              <form onSubmit={handleSubmit}>
                <input ref={inputRef} value={input} placeholder="Say something..." readOnly={thread.isLoading} disabled={thread.isLoading} onChange={(e) => setInput(e.target.value)} />
              </form>
            </div>
          );
        }
        ```
      </Tab>

      <Tab title="LlamaIndex" icon="https://mintlify-assets.b-cdn.net/auth0/llamadex.svg">
        <GitHubPrereqs lang="python" />

        ### 1. Configure Auth0 AI

        First, you must install the SDK:

        ```bash wrap lines theme={null}
        pip install auth0-ai-llamaindex
        ```

        Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required GitHub scopes.

        ```python ./src/lib/auth0-ai.py wrap lines theme={null}
        from auth0_ai_llamaindex.auth0_ai import Auth0AI
        from flask import session

        auth0_ai = Auth0AI()

        with_github = auth0_ai.with_token_vault(
            connection="github",
            scopes=["repo"],
            refresh_token=lambda *_args, **_kwargs:session["user"]["refresh_token"],
        )
        ```

        <Info>
          Here, the session is controlled by a Flask application instance. You may utilize any other framework or session store of your preference.
        </Info>

        ### 2. Integrate your tool with GitHub

        Wrap your tool using the Auth0 AI SDK to obtain an access token for the GitHub API.

        ```python ./src/lib/tools/list_repositories.py wrap lines highlight={4-5,9,19,21} theme={null}
        from github import Github
        from github.GithubException import BadCredentialsException
        from llama_index.core.tools import FunctionTool
        from auth0_ai_llamaindex.token_vault import get_access_token_from_token_vault, TokenVaultError
        from src.lib.auth0_ai import with_github

        def list_github_repositories_tool_function():
            # Get the access token from Auth0 AI
            access_token = get_access_token_from_token_vault()

            # GitHub SDK
            try:
                g = Github(access_token)
                user = g.get_user()
                repos = user.get_repos()
                repo_names = [repo.name for repo in repos]
                return repo_names
            except BadCredentialsException:
                raise TokenVaultError("Authorization required to access the Token Vault API")

        list_github_repositories_tool = with_github(FunctionTool.from_defaults(
            name="list_github_repositories",
            description="List respositories for the current user on GitHub",
            fn=list_github_repositories_tool_function,
        ))
        ```

        Now that the tool is protected, you can pass it your LlamaIndex agent.

        ```python ./src/lib/agent.ts wrap lines highlight={3,13} theme={null}
        from datetime import datetime
        from llama_index.agent.openai import OpenAIAgent
        from src.lib.tools.list_repositories import list_github_repositories_tool

        system_prompt = f"""You are an assistant designed to answer random user's questions.
        **Additional Guidelines**:
        - Today’s date for reference: {datetime.now().isoformat()}
        """

        agent = OpenAIAgent.from_tools(
            tools=[
                # a tool with Token Vault access
                list_github_repositories_tool
                # ... other tools
            ],
            model="gpt-4o",
            system_prompt=system_prompt
            verbose=True,
        )
        ```

        ### 3. Handle authentication redirects

        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 such authentication redirects integrated with the LLamaIndex SDK.

        #### Server side

        On the server side of your Flask application you will need to set up a route to handle the Chat API requests. This route will be responsible for forwarding the requests to the OpenAI API utilizing LlamaIndex's SDK, that has been initialized with Auth0 AI's protection enhancements for tools.

        When `TokenVaultInterrupt` error occurs, the server side will signal the front-end about the level access restrictions, and the front-end should prompt the user to trigger a new authorization (or login) request with the necessary permissions.

        ```python ./src/app.py wrap lines highlight={3-5,19-20} theme={null}
        from dotenv import load_dotenv
        from flask import Flask, request, jsonify, session
        from auth0_ai_llamaindex.auth0_ai import Auth0AI
        from auth0_ai_llamaindex.token_vault import TokenVaultInterrupt
        from src.lib.agent import agent

        load_dotenv()
        app = Flask(__name__)

        @app.route("/chat", methods=["POST"])
        async def chat():
            if "user" not in session:
                return jsonify({"error": "unauthorized"}), 401

            try:
                message = request.json.get("message")
                response = agent.achat(message)
                return jsonify({"response": str(response)})
            except TokenVaultInterrupt as e:
                return jsonify({"error": str(e.to_json())}), 403
            except Exception as e:
                return jsonify({"error": str(e)}), 500
        ```
      </Tab>

      <Tab title="CrewAI" icon="https://mintlify-assets.b-cdn.net/auth0/crew_ai.svg" disabled={true}>
        comming soon!
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

<AccountLinking connectionLabel="GitHub" />
