# How the Composio Connector Handles OAuth for Integrations in OpenHuman

> Discover how the Composio connector uses a three-stage OAuth flow across TypeScript frontend and Rust core RPC for secure third-party tool integrations in OpenHuman.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-06-16

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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:

```typescript
// 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');
}

```

```rust
// 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}")]))
}

```

```rust
// 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`](https://github.com/tinyhumansai/openhuman/blob/main/composioApi.ts) initiates OAuth via JSON-RPC calls to the Rust core.
- The **core dispatcher** in [`connections.rs`](https://github.com/tinyhumansai/openhuman/blob/main/connections.rs) selects between backend-proxied and direct authorization modes.
- **Meta-specific protection** in [`oauth_handoff.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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.