Manually authenticating users

Manual authentication lets you connect users to toolkits outside of the chat flow. Use this when you want to:

  • Pre-authenticate users before they start chatting
  • Build a custom connections UI in your app

Authorize a toolkit

Use session.authorize() to generate a Connect Link URL, redirect the user, and wait for them to complete:

1session = composio.create(user_id="user_123")
2
3connection_request = session.authorize("gmail")
4
5print(connection_request.redirect_url)
6# https://connect.composio.dev/link/ln_abc123
7
8connected_account = connection_request.wait_for_connection(60000)
9print(f"Connected: {connected_account.id}")

Redirect the user to the redirect URL. After they authenticate, they’ll return to your callback URL. The connection request polls until the user completes authentication (default timeout: 60 seconds).

If the user closes the Connect Link without completing auth, the connection remains in INITIATED status until it expires.

Check connection status

Use session.toolkits() to see all toolkits in the session and their connection status:

1toolkits = session.toolkits()
2
3for toolkit in toolkits.items:
4 status = toolkit.connection.connected_account.id if toolkit.connection.is_active else "Not connected"
5 print(f"{toolkit.name}: {status}")

Disabling in-chat auth

By default, Tool Router includes the COMPOSIO_MANAGE_CONNECTIONS meta-tool that prompts users to authenticate during chat. To disable this and handle auth entirely in your UI:

1session = composio.create(
2 user_id="user_123",
3 manage_connections=False,
4)

Putting it together

A common pattern is to verify all required connections before starting the agent:

1from composio import Composio
2
3composio = Composio(api_key="your-api-key")
4
5required_toolkits = ["gmail", "github"]
6
7session = composio.create(
8 user_id="user_123",
9 manage_connections=False, # Disable in-chat auth prompts
10)
11
12toolkits = session.toolkits()
13
14connected = {t.slug for t in toolkits.items if t.connection.is_active}
15pending = [slug for slug in required_toolkits if slug not in connected]
16
17print(f"Connected: {connected}")
18print(f"Pending: {pending}")
19
20for slug in pending:
21 connection_request = session.authorize(slug)
22 print(f"Connect {slug}: {connection_request.redirect_url}")
23 connection_request.wait_for_connection()
24
25print(f"All toolkits connected! MCP URL: {session.mcp.url}")