How the Composio Connector Handles OAuth for Integrations in OpenHuman

The Composio connector implements a three-stage OAuth flow that spans the TypeScript frontend, Rust core RPC layer, and specialized Meta-specific rate-limit mitigation logic to safely authorize integrations with third-party toolkits.

The OpenHuman repository by tinyhumansai coordinates OAuth authentication across multiple architectural layers. Understanding how the Composio connector handles OAuth for integrations requires examining the interaction between the frontend RPC wrapper, the core dispatch logic, and the defensive programming patterns designed for Meta-hosted services like Instagram and Facebook.

Frontend RPC Wrapper

The OAuth flow begins in app/src/lib/composio/composioApi.ts, where the frontend exposes an authorize() function. This TypeScript wrapper constructs a JSON-RPC request and forwards it to the Rust core using callCoreRpc.

When a user initiates a connection, the UI calls authorize(toolkit, extraParams?) with optional parameters—for example, passing waba_id for WhatsApp Business integrations. The function returns a connectUrl that the browser opens to begin the OAuth handshake with the third-party provider.

Core RPC Dispatch

The JSON-RPC request arrives at the Rust backend as openhuman.composio_authorize, implemented in src/openhuman/composio/ops/connections.rs. This function orchestrates the authorization strategy based on the configured client mode.

Backend Mode Authorization

When operating in backend-proxied mode, the system calls authorize_with_meta_guard from oauth_handoff.rs. This approach delegates the OAuth handshake to the backend service via the /agent-integrations/composio/authorize endpoint, providing a centralized point for credential management and rate-limit handling.

Direct Mode Authorization

For direct mode configurations, the core invokes direct_authorize_with_meta_guard. This path uses the same rate-limit protection logic but communicates directly with Composio's APIs rather than proxying through the backend infrastructure.

Event Publishing

After successful authorization, the core publishes a ComposioConnectionCreated domain event via the global event bus. This notification allows the UI and other system components to synchronize state, update connection lists, and trigger subsequent workflows.

OAuth Handoff and Rate-Limit Mitigation

The helper module src/openhuman/composio/oauth_handoff.rs contains critical defensive logic for Meta-hosted toolkits. These integrations are particularly susceptible to rate limiting, requiring proactive connection management.

Stale Connection Cleanup

Before initiating new OAuth flows for Instagram or Facebook, the system calls clear_non_active_connections. This function removes pending or abandoned connection rows that would otherwise accumulate and trigger HTTP 429 responses from Meta's APIs.

Exponential Backoff Strategy

The authorize_with_rate_limit_retry function wraps the authorization call with exponential backoff logic. When is_authorize_rate_limited detects a rate-limit error, the system retries the request with increasing delays and provides user-friendly error messages via meta_oauth_rate_limit_message.

Complete Flow Example

The following code demonstrates the end-to-end OAuth initialization:

// Frontend: Initiate OAuth from the UI
import { authorize } from '@/lib/composio/composioApi';

async function startComposioOAuth(toolkit: string) {
  // Optional extraParams for specific toolkits like WhatsApp
  const resp = await authorize(toolkit, { waba_id: '12345' });
  // Open the OAuth consent URL in a new browser tab
  window.open(resp.connectUrl, '_blank');
}
// Core: Handle authorization with mode selection and event emission
pub async fn composio_authorize(
    config: &Config,
    toolkit: &str,
    extra_params: Option<serde_json::Value>,
) -> OpResult<RpcOutcome<ComposioAuthorizeResponse>> {
    let kind = create_composio_client(config)?;
    let resp = match kind {
        ComposioClientKind::Backend(client) => {
            // Backend flow with Meta-specific guards
            oauth_handoff::authorize_with_meta_guard(&client, toolkit, extra_params).await?
        }
        ComposioClientKind::Direct(direct) => {
            // Direct flow with same protection
            oauth_handoff::direct_authorize_with_meta_guard(&direct, toolkit, &config.composio.entity_id).await?
        }
    };

    // Publish event for UI synchronization
    crate::core::event_bus::publish_global(
        crate::core::event_bus::DomainEvent::ComposioConnectionCreated {
            toolkit: toolkit.to_string(),
            connection_id: resp.connection_id.clone(),
            connect_url: resp.connect_url.clone(),
        },
    );

    Ok(RpcOutcome::new(resp, vec![format!("composio: authorize flow started for {toolkit}")]))
}
// Handoff: Meta-specific cleanup and retry logic
pub async fn authorize_with_meta_guard(
    client: &ComposioClient,
    toolkit: &str,
    extra_params: Option<serde_json::Value>,
) -> anyhow::Result<ComposioAuthorizeResponse> {
    // Prevent rate limits by clearing stale pending connections
    let _cleared = clear_non_active_connections(client, toolkit).await?;
    // Execute with automatic retry on 429 errors
    authorize_with_rate_limit_retry(|| client.authorize(toolkit, extra_params.clone())).await
}

Summary

  • The frontend RPC wrapper in composioApi.ts initiates OAuth via JSON-RPC calls to the Rust core.
  • The core dispatcher in connections.rs selects between backend-proxied and direct authorization modes.
  • Meta-specific protection in oauth_handoff.rs clears stale connections and implements exponential backoff to prevent 429 errors.
  • The system emits ComposioConnectionCreated events to keep the UI synchronized with backend state.
  • Optional extraParams support allows toolkit-specific configuration (e.g., WhatsApp Business Account IDs).

Frequently Asked Questions

How does the Composio connector handle rate limits during OAuth?

The connector implements proactive rate-limit mitigation in src/openhuman/composio/oauth_handoff.rs. Before authorizing Meta toolkits like Instagram or Facebook, it clears non-active connection rows to prevent accumulation. If a 429 error occurs, the authorize_with_rate_limit_retry function applies exponential backoff and returns user-friendly error messages.

What is the difference between backend and direct authorization modes?

Backend mode proxies OAuth requests through the OpenHuman backend infrastructure using authorize_with_meta_guard, enabling centralized credential management. Direct mode uses direct_authorize_with_meta_guard to communicate straight with Composio's APIs. Both modes apply identical Meta-specific rate-limit protection but differ in network topology and security boundaries.

How does the frontend know when OAuth authorization is complete?

The Rust core publishes a ComposioConnectionCreated domain event via the global event bus immediately after successful authorization. This event contains the connection_id, toolkit name, and connect_url, allowing the TypeScript frontend to update its state and notify the user without polling.

Can I pass additional parameters to the Composio authorize function?

Yes. The authorize() function in composioApi.ts accepts an optional extraParams object that forwards toolkit-specific configuration to the backend. For example, WhatsApp integrations require a waba_id (WhatsApp Business Account ID) parameter, which travels through the JSON-RPC request to the Rust core and ultimately to the Composio API.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →