# How Composio Handles Authentication with Connected Accounts and OAuth Flows

> Learn how Composio handles authentication for connected accounts and OAuth flows. It manages the entire lifecycle from initiation to token refresh for third-party services.

- Repository: [Composio/composio](https://github.com/composiohq/composio)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Composio orchestrates OAuth authentication by treating every third-party service as a connected account linked to an auth config, providing SDK methods that handle the entire lifecycle from initiation to token refresh.**

Composio provides a type-safe, uniform layer for managing authentication with external APIs. Whether integrating GitHub, Gmail, or custom services, the SDK abstracts OAuth complexity through a consistent **connected account** model defined in the `ComposioHQ/composio` repository.

## Understanding Composio's Authentication Architecture

### Auth Configs and Supported Schemes

Every connection starts with an **auth config** that defines the authentication scheme. The `AuthSchemeTypes` enum in [`ts/packages/core/src/types/authConfigs.types.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/types/authConfigs.types.ts) enumerates all supported protocols, including OAuth 1, OAuth 2, API-Key, Basic, Bearer-Token, and Google service accounts.

```typescript
// ts/packages/core/src/types/authConfigs.types.ts#L9-L24
export enum AuthSchemeTypes {
  OAUTH1 = 'OAUTH1',
  OAUTH2 = 'OAUTH2',
  API_KEY = 'API_KEY',
  BASIC = 'BASIC',
  BEARER_TOKEN = 'BEARER_TOKEN',
  GOOGLE_SERVICE_ACCOUNT = 'GOOGLE_SERVICE_ACCOUNT',
  // ...
}

```

This centralized definition allows the SDK to handle any provider using the same orchestration logic, regardless of the underlying protocol.

## Implementing OAuth Flows with Connected Accounts

### Initiating a Connection Request

The `ConnectedAccounts` class in [`ts/packages/core/src/models/ConnectedAccounts.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/models/ConnectedAccounts.ts) provides the `initiate()` method to start a full-stack OAuth flow. This method POSTs to `/connected-accounts` and returns a `ConnectionRequest` object containing the **redirect URL** where users authenticate with the provider.

```typescript
// ts/packages/core/src/models/ConnectedAccounts.ts#L58-L66
async initiate(
  userId: string,
  authConfigId: string,
  options?: { callbackUrl?: string; redirectUrl?: string }
): Promise<ConnectionRequest> {
  const response = await this.apiClient.post('/connected-accounts', {
    userId,
    authConfigId,
    ...options
  });
  return createConnectionRequest(response.data, this.apiClient);
}

```

### The Connect Link Shortcut

For simpler integrations, the `link()` method (lines 152-166 in the same file) creates a lightweight "Connect-Link" flow via POST `/link/create`. This alternative generates a pre-authenticated URL that can be embedded directly in UI elements or shared via messaging platforms, bypassing the need for complex backend state management.

### Polling for Completion

Once the user is redirected to the provider, the SDK handles completion through the `ConnectionRequest` class in [`ts/packages/core/src/models/ConnectionRequest.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/models/ConnectionRequest.ts). The `waitForConnection()` method implements a polling loop that repeatedly calls `GET /connected-accounts/{id}` until the status transitions to `ACTIVE` or a terminal error state occurs.

```typescript
// ts/packages/core/src/models/ConnectionRequest.ts#L30-L55
async waitForConnection(timeoutMs: number = 60000): Promise<ConnectedAccount> {
  const startTime = Date.now();
  const pollInterval = 2000; // 2 seconds
  
  while (Date.now() - startTime < timeoutMs) {
    const status = await this.checkStatus(); // GET /connected-accounts/{this.id}
    
    if (status === 'ACTIVE') {
      return this.getConnectedAccount();
    }
    
    if (['ERROR', 'REVOKED'].includes(status)) {
      throw new ComposioConnectionError(`Connection failed with status: ${status}`);
    }
    
    await new Promise(resolve => setTimeout(resolve, pollInterval));
  }
  
  throw new ComposioTimeoutError('Connection polling timed out');
}

```

## Code Implementation Examples

### Full OAuth2 Flow Implementation

This example demonstrates creating an auth config, initiating the flow, and waiting for completion:

```typescript
import { Composio, AuthSchemeTypes } from '@composio/core';

const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });

async function connectGitHubUser(userId: string) {
  // 1. Create OAuth2 auth config (typically done once per integration)
  const authConfig = await composio.authConfigs.create({
    type: 'use_custom_auth',
    name: 'GitHub OAuth',
    authScheme: AuthSchemeTypes.OAUTH2,
    credentials: {
      client_id: process.env.GITHUB_CLIENT_ID,
      client_secret: process.env.GITHUB_CLIENT_SECRET,
      scopes: ['repo', 'read:user'],
    },
  });

  // 2. Initiate connection request
  const request = await composio.connectedAccounts.initiate(
    userId,
    authConfig.id,
    {
      callbackUrl: 'https://myapp.com/composio/callback',
    }
  );

  // 3. Redirect user to provider consent page
  console.log('Authorization URL:', request.redirectUrl);
  
  // 4. Poll until user completes authorization
  const connectedAccount = await request.waitForConnection(120000);
  
  console.log('Connected Account ID:', connectedAccount.id);
  console.log('Toolkit:', connectedAccount.toolkit.slug);
  
  return connectedAccount;
}

```

### Connect Link Implementation

For scenarios requiring minimal backend logic, use the `link()` method:

```typescript
async function generateConnectLink(userId: string, authConfigId: string) {
  const linkRequest = await composio.connectedAccounts.link(
    userId,
    authConfigId,
    {
      callbackUrl: 'https://myapp.com/composio/callback',
      // Optional: customize redirect after completion
      redirectUrl: 'https://myapp.com/dashboard',
    }
  );

  // Returns a pre-authenticated URL suitable for emails or UI buttons
  return {
    connectUrl: linkRequest.redirectUrl,
    requestId: linkRequest.id,
  };
}

```

## Key Source Files and Architecture

Understanding the codebase structure helps when debugging or extending Composio's authentication capabilities:

| File | Responsibility |
|------|----------------|
| [`ts/packages/core/src/types/authConfigs.types.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/types/authConfigs.types.ts) | Defines `AuthSchemeTypes` enum and auth config payload shapes |
| [`ts/packages/core/src/models/ConnectedAccounts.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/models/ConnectedAccounts.ts) | Core class implementing `initiate()` and `link()` methods for creating connection requests |
| [`ts/packages/core/src/models/ConnectionRequest.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/models/ConnectionRequest.ts) | Implements polling logic via `waitForConnection()` and status checking |
| [`ts/packages/core/src/utils/transformers/connectedAccounts.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/utils/transformers/connectedAccounts.ts) | Transforms raw API responses into SDK-friendly `ConnectedAccount` objects |
| `ts/packages/core/src/errors/` | Contains specific error types like `ComposioFailedToCreateConnectedAccountLink` and `ComposioConnectedAccountNotFoundError` |

## Summary

- **Composio** unifies third-party authentication through a **connected account** model linked to **auth configs** that define schemes like OAuth 2, OAuth 1, API keys, and basic auth.
- The **SDK orchestrates the full OAuth lifecycle**: creating connection requests via `ConnectedAccounts.initiate()` or `ConnectedAccounts.link()`, redirecting users to provider consent pages, and polling for completion through `ConnectionRequest.waitForConnection()`.
- **Type safety** is enforced through the `AuthSchemeTypes` enum in [`authConfigs.types.ts`](https://github.com/ComposioHQ/composio/blob/main/authConfigs.types.ts), ensuring consistent handling across all supported providers.
- The polling mechanism in [`ConnectionRequest.ts`](https://github.com/ComposioHQ/composio/blob/main/ConnectionRequest.ts) automatically handles the transition from `PENDING` to `ACTIVE` status, returning fully initialized account objects with valid tokens.

## Frequently Asked Questions

### What authentication schemes does Composio support?

Composio supports **OAuth 1**, **OAuth 2**, **API Key**, **Basic authentication**, **Bearer Token**, and **Google Service Account** authentication. These schemes are defined in the `AuthSchemeTypes` enum located in [`ts/packages/core/src/types/authConfigs.types.ts`](https://github.com/ComposioHQ/composio/blob/main/ts/packages/core/src/types/authConfigs.types.ts), allowing the SDK to handle any third-party provider using the same consistent interface regardless of the underlying protocol.

### How does the SDK know when a user has completed OAuth authorization?

The SDK implements a **polling mechanism** through the `waitForConnection()` method in the `ConnectionRequest` class. After initiating a connection, the SDK repeatedly calls `GET /connected-accounts/{id}` every 2 seconds until the status changes to `ACTIVE`, indicating successful authorization. If the status reaches an error state or the timeout expires (default 60 seconds), the method throws a specific error from the `ts/packages/core/src/errors/` directory.

### What is the difference between `initiate()` and `link()` methods?

The **`initiate()`** method creates a full-stack connection request via POST `/connected-accounts`, suitable for backend-driven OAuth flows where you manage the state and callback handling. The **`link()`** method creates a lightweight "Connect-Link" via POST `/link/create`, generating a pre-authenticated URL ideal for sharing in emails, embedding in UI buttons, or scenarios requiring minimal backend logic. Both return a `ConnectionRequest` object supporting `waitForConnection()`.

### Where are OAuth credentials stored in the Composio architecture?

OAuth credentials are stored within **auth configs** created through the SDK or API. When you call `composio.authConfigs.create()`, you provide credentials like `client_id` and `client_secret` which are persisted on Composio's backend and associated with your integration. During the OAuth flow, the backend uses these stored credentials to negotiate with the provider, while the SDK only handles the orchestration and polling via the `ConnectedAccounts` and `ConnectionRequest` models.