--- title: "Custom Auth Flow with CrewAI" description: "Learn how to create a custom auth flow with CrewAI" --- import { Steps, Callout } from "nextra/components"; import ToggleContent from "@/app/_components/toggle-content"; ## Custom Auth Flow with CrewAI In this guide, we will explore how to create a custom auth flow that will be performed before executing Arcade tools within your CrewAI agent team. The `ArcadeToolManager`'s built-in authorization and tool execution flows work well for many typical use cases. However, some scenarios call for a tailored approach. By implementing a custom auth flow, you gain flexibility in handling tool authorization. If your use case calls for a unique interface, additional approval steps, or specialized error handling, then this guide is for you. ### Prerequisites - [Obtain an Arcade API key](/get-started/setup/api-keys) ### Set up your environment Install the required package, and ensure your environment variables are set with your Arcade and OpenAI API keys: ```bash pip install crewai-arcade ``` ### Configure API keys Provide your Arcade and OpenAI API keys. You can store them in environment variables like so: ```bash export ARCADE_API_KEY="your_arcade_api_key" export OPENAI_API_KEY="your_openai_api_key" ``` ### Define your custom auth flow The custom auth flow defined in the following code snippet is a function that will be called whenever CrewAI needs to call a tool. ```python from typing import Any from crewai_arcade import ArcadeToolManager USER_ID = "{arcade_user_id}" def custom_auth_flow( manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any] ) -> Any: """Custom auth flow for the ArcadeToolManager This function is called when CrewAI needs to call a tool that requires authorization. Authorization is handled before executing the tool. This function overrides the ArcadeToolManager's default auth flow performed by ArcadeToolManager.authorize_tool """ # Get authorization status auth_response = manager.authorize(tool_name, USER_ID) # If the user is not authorized for the tool, # then we need to handle the authorization before executing the tool if not manager.is_authorized(auth_response.id): print(f"Authorization required for tool: '{tool_name}' with inputs:") for input_name, input_value in tool_input.items(): print(f" {input_name}: {input_value}") # Handle authorization print(f"\nTo authorize, visit: {auth_response.url}") # Block until the user has completed the authorization auth_response = manager.wait_for_auth(auth_response) # Ensure authorization completed successfully if not manager.is_authorized(auth_response.id): raise ValueError(f"Authorization failed for {tool_name}. URL: {auth_response.url}") else: print(f"Authorization already granted for tool: '{tool_name}' with inputs:") for input_name, input_value in tool_input.items(): print(f" {input_name}: {input_value}") def tool_manager_callback(tool_manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any]) -> Any: """Tool executor callback with custom auth flow for the ArcadeToolManager ArcadeToolManager's default executor handles authorization and tool execution. This function overrides the default executor to handle authorization in a custom way and then executes the tool. """ custom_auth_flow(tool_manager, tool_name, **tool_input) return tool_manager.execute_tool(USER_ID, tool_name, **tool_input) ``` ### Get Arcade tools You can now provide the tool manager callback to the `ArcadeToolManager` upon initialization: ```python # Provide the tool manager callback to the ArcadeToolManager manager = ArcadeToolManager(executor=tool_manager_callback) # Retrieve the provided tools and/or MCP Servers as CrewAI StructuredTools. tools = manager.get_tools(tools=["Gmail.ListEmails"], toolkits=["Slack"]) ``` ### Use tools in your CrewAI agent team Create a Crew that uses your tools with the custom auth flow. When the tool is called, your tool manager callback will be called to handle the authorization and then the tool will be executed. ```python from crewai import Agent, Crew, Task from crewai.llm import LLM crew_agent = Agent( role="Main Agent", backstory="You are a helpful assistant", goal="Help the user with their requests", tools=tools, allow_delegation=False, verbose=True, llm=LLM(model="gpt-4o"), ) task = Task( description="Get the 5 most recent emails from the user's inbox and summarize them and recommend a response for each.", expected_output="A bulleted list with a one sentence summary of each email and a recommended response to the email.", agent=crew_agent, tools=crew_agent.tools, ) crew = Crew( agents=[crew_agent], tasks=[task], verbose=True, memory=True, ) result = crew.kickoff() print("\n\n\n ------------ Result ------------ \n\n\n") print(result) ``` ```python from typing import Any from crewai import Agent, Crew, Task from crewai.llm import LLM from crewai_arcade import ArcadeToolManager USER_ID = "{arcade_user_id}" def custom_auth_flow( manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any] ) -> Any: """Custom auth flow for the ArcadeToolManager This function is called when CrewAI needs to call a tool that requires authorization. Authorization is handled before executing the tool. This function overrides the ArcadeToolManager's default auth flow performed by ArcadeToolManager.authorize_tool """ # Get authorization status auth_response = manager.authorize(tool_name, USER_ID) # If the user is not authorized for the tool, # then we need to handle the authorization before executing the tool if not manager.is_authorized(auth_response.id): print(f"Authorization required for tool: '{tool_name}' with inputs:") for input_name, input_value in tool_input.items(): print(f" {input_name}: {input_value}") # Handle authorization print(f"\nTo authorize, visit: {auth_response.url}") # Block until the user has completed the authorization auth_response = manager.wait_for_auth(auth_response) # Ensure authorization completed successfully if not manager.is_authorized(auth_response.id): raise ValueError(f"Authorization failed for {tool_name}. URL: {auth_response.url}") else: print(f"Authorization already granted for tool: '{tool_name}' with inputs:") for input_name, input_value in tool_input.items(): print(f" {input_name}: {input_value}") def tool_manager_callback(tool_manager: ArcadeToolManager, tool_name: str, **tool_input: dict[str, Any]) -> Any: """Tool executor callback with custom auth flow for the ArcadeToolManager ArcadeToolManager's default executor handles authorization and tool execution. This function overrides the default executor to handle authorization in a custom way and then executes the tool. """ custom_auth_flow(tool_manager, tool_name, **tool_input) return tool_manager.execute_tool(USER_ID, tool_name, **tool_input) manager = ArcadeToolManager(executor=tool_manager_callback) tools = manager.get_tools(tools=["Gmail.ListEmails"], toolkits=["Slack"]) crew_agent = Agent( role="Main Agent", backstory="You are a helpful assistant", goal="Help the user with their requests", tools=tools, allow_delegation=False, verbose=True, llm=LLM(model="gpt-4o"), ) task = Task( description="Get the 5 most recent emails from the user's inbox and summarize them and recommend a response for each.", expected_output="A bulleted list with a one sentence summary of each email and a recommended response to the email.", agent=crew_agent, tools=crew_agent.tools, ) crew = Crew( agents=[crew_agent], tasks=[task], verbose=True, memory=True, ) result = crew.kickoff() print("\n\n\n ------------ Result ------------ \n\n\n") print(result) ``` ## Next steps Now you're ready to integrate Arcade tools with a custom auth flow into your own CrewAI agent team.