Introduction

Securely connect your AI agents to user accounts on external apps

AI agents often need to perform actions on behalf of users like;

  • sending an email from their Gmail
  • creating an issue in their Jira
  • or posting to their Slack

Doing this securely requires handling complex authentication flows like OAuth 2.0, managing API keys, storing sensitive tokens, and refreshing credentials. This distracts from building the core agent logic.

Let’s see how Composio Auth works in a basic example where we connect a user to their GitHub account.

Quickstart with Composio Auth

1. Identify the User (Entity) & App

Composio lets you specify a unique entity_id for each user in your application. This is the user’s identifier in your application.

1# User identifier from your application
2user_id_in_my_app = "user-alice-456" # Can be UUID from DB
3app_to_connect = "github" # The app key

2. Initiate the Connection

You’ll need the integration_id for the app (which you typically set up once - see Integrations) and the entity_id you specified for your user.

1from composio_openai import ComposioToolSet, Action # Or framework-specific ToolSet
2
3toolset = ComposioToolSet()
4entity = toolset.get_entity(id=user_id_in_my_app) # Get Entity object
5
6print(f"Initiating GitHub connection for entity: {entity.id}")
7# Initiate connection using the app's Integration and the user's Entity ID
8connection_request = entity.initiate_connection(app_name=app_to_connect)
9
10# Composio returns a redirect URL for OAuth flows
11if connection_request.redirectUrl:
12 print(f"Please direct the user to visit: {connection_request.redirectUrl}")

3. Wait for Connection Activation (OAuth)

For OAuth flows, the user needs to visit the redirectUrl and authorize the connection. Your application can wait for the connection to become active.

1# Wait for the user to complete the OAuth flow in their browser
2print("Waiting for connection to become active...")
3try:
4 # This polls until the connection status is ACTIVE or timeout occurs
5 active_connection = connection_request.wait_until_active(
6 client=toolset.client, # Pass the underlying client
7 timeout=120 # Wait for up to 2 minutes
8 )
9 print(f"Connection successful! ID: {active_connection.id}")
10 # Store active_connection.id associated with user_id_in_my_app
11except Exception as e:
12 print(f"Connection timed out or failed: {e}")

4. Execute Actions Using the Connection

Once the connection is active, you (or realistically, an agent) can execute actions for that app on behalf of that specific user by providing their entity_id

1# Execute using the user's entity_id (Composio finds the right connection)
2print(f"\nFetching GitHub username for entity: {user_id_in_my_app}")
3user_info = toolset.execute_action(
4 action=Action.GITHUB_GET_THE_AUTHENTICATED_USER,
5 params={},
6 entity_id=user_id_in_my_app # Specify the user context
7)
8
9if user_info.get("successful"):
10 print("GitHub username:", user_info.get("data", {}).get("login"))
11else:
12 print("Failed to fetch user:", user_info.get("error"))

Alternatively, you can execute actions directly using the connection ID if you have it:

1user_info_direct = toolset.execute_action(
2 action=Action.GITHUB_GET_THE_AUTHENTICATED_USER,
3 params={},
4 connected_account_id=active_connection.id
5)

This flow demonstrates how Composio uses Integrations (app config), Entities (your users), and Connections (the secure link between them) to simplify authenticated interactions for your AI agents.

Composio Auth Concepts Diagram

1# filename: connect_and_fetch_github.py
2from composio_openai import ComposioToolSet, Action, App
3from dotenv import load_dotenv
4import os
5import sys
6import time
7
8# Load environment variables from .env file
9# Ensure COMPOSIO_API_KEY is set
10load_dotenv()
11
12def run_auth_flow():
13 # --- 1. Identify User & App ---
14 user_id_in_my_app = "user-quickstart-py-example" # Example user ID
15 app_to_connect = App.GITHUB # Use Enum for clarity
16
17 print(f"--- Starting GitHub connection for Entity: {user_id_in_my_app} ---")
18
19 toolset = ComposioToolSet()
20 entity = toolset.get_entity(id=user_id_in_my_app)
21
22 active_connection = None # Initialize variable
23
24 try:
25 # --- 2. Initiate Connection ---
26 print(f"Initiating {app_to_connect.value} connection...")
27 # Use app_name; SDK finds appropriate integration
28 connection_request = entity.initiate_connection(app_name=app_to_connect)
29
30 # --- 3. Handle Redirect & Wait for Activation (OAuth) ---
31 if connection_request.redirectUrl:
32 print("\n!!! ACTION REQUIRED !!!")
33 print(f"Please visit this URL to authorize the connection:\n{connection_request.redirectUrl}\n")
34 print("Waiting for connection to become active (up to 120 seconds)...")
35
36 try:
37 # Poll Composio until the connection is marked active
38 active_connection = connection_request.wait_until_active(
39 client=toolset.client, # Pass the underlying client
40 timeout=120
41 )
42 print(f"\nConnection successful! ID: {active_connection.id}")
43 # In a real app, you'd store active_connection.id linked to user_id_in_my_app
44 except Exception as e:
45 print(f"Error waiting for connection: {e}", file=sys.stderr)
46 print("Please ensure you visited the URL and approved the connection.")
47 return # Exit if connection failed
48
49 else:
50 # Handle non-OAuth flows if needed (e.g., API Key where connection is instant)
51 print("Connection established (non-OAuth flow). Fetching details...")
52 # Fetch the connection details using the ID from the request
53 active_connection = toolset.client.connected_accounts.get(connection_id=connection_request.connectedAccountId)
54 if active_connection.status != "ACTIVE":
55 print(f"Connection is not active (Status: {active_connection.status}). Exiting.", file=sys.stderr)
56 return
57
58
59 # --- 4. Execute Action ---
60 if active_connection and active_connection.status == "ACTIVE":
61 print(f"\nExecuting action using connection ID: {active_connection.id}")
62 print(f"Fetching GitHub username for entity: {user_id_in_my_app}...")
63
64 user_info = toolset.execute_action(
65 action=Action.GITHUB_GET_THE_AUTHENTICATED_USER,
66 params={},
67 # Provide context via entity_id (recommended)
68 entity_id=user_id_in_my_app
69 # OR precisely target the connection (if ID was stored)
70 # connected_account_id=active_connection.id
71 )
72
73 print("\n--- Execution Result ---")
74 if user_info.get("successful"):
75 username = user_info.get("data", {}).get("login", "N/A")
76 print(f"Successfully fetched GitHub username: {username}")
77 else:
78 print(f"Failed to fetch user info: {user_info.get('error', 'Unknown error')}")
79 # import json
80 # print("\nFull response:")
81 # print(json.dumps(user_info, indent=2))
82 else:
83 print("\nSkipping action execution as connection is not active.")
84
85
86 except Exception as e:
87 print(f"\nAn unexpected error occurred: {e}", file=sys.stderr)
88
89if __name__ == "__main__":
90 run_auth_flow()