Adds composio-sdk/ with SKILL.md, AGENTS.md, and 18 rule files covering Tool Router, direct execution, triggers, and auth patterns. Source: composiohq/skills Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
235 lines
6.3 KiB
Markdown
235 lines
6.3 KiB
Markdown
---
|
|
title: Connected Accounts Management
|
|
impact: HIGH
|
|
description: Comprehensive guide to CRUD operations on connected accounts with emphasis on secure authentication flows
|
|
tags: [connected-accounts, authentication, oauth, crud, security]
|
|
---
|
|
|
|
# Connected Accounts Management
|
|
|
|
> **Using Tool Router?** If you're using Tool Router, you can use `session.toolkits()` to view the auth configs and connected accounts being used by the Tool Router. You only need to use the methods below if you're managing connected accounts outside of Tool Router.
|
|
|
|
Connected accounts store authentication tokens for external services. Use the `connectedAccounts` API for CRUD operations.
|
|
|
|
## Create Connected Accounts
|
|
|
|
### Recommended: link() - Composio-Hosted Authentication
|
|
|
|
Use `link()` for most flows. Composio handles security, OAuth, and form rendering.
|
|
|
|
```typescript
|
|
const connectionRequest = await composio.connectedAccounts.link(
|
|
'user_123',
|
|
'auth_config_123',
|
|
{ callbackUrl: 'https://your-app.com/callback' }
|
|
);
|
|
|
|
// Redirect user to authentication page
|
|
window.location.href = connectionRequest.redirectUrl;
|
|
|
|
// Wait for completion
|
|
const account = await connectionRequest.waitForConnection();
|
|
```
|
|
|
|
**Why use link():**
|
|
- Handles OAuth security and form UI
|
|
- Works with 200+ services
|
|
- Whitelabel with your app name/logo (Project Settings on dashboard)
|
|
- No custom UI needed
|
|
|
|
### Advanced: initiate() - Custom Authentication UI
|
|
|
|
Only use when building custom auth interfaces:
|
|
|
|
```typescript
|
|
// API Key (custom form)
|
|
const connection = await composio.connectedAccounts.initiate(
|
|
'user_123',
|
|
'auth_config_456',
|
|
{
|
|
config: AuthScheme.ApiKey({ api_key: apiKey }),
|
|
}
|
|
);
|
|
|
|
// OAuth with extra params (Zendesk, PostHog, etc.)
|
|
const connection = await composio.connectedAccounts.initiate(
|
|
'user_123',
|
|
'zendesk_config',
|
|
{
|
|
config: AuthScheme.OAuth2({ subdomain: "your_subdomain" })
|
|
}
|
|
);
|
|
window.location.href = connection.redirectUrl;
|
|
```
|
|
|
|
**AuthScheme helpers:**
|
|
- `AuthScheme.OAuth2({ subdomain: 'example' })`
|
|
- `AuthScheme.ApiKey({ api_key: 'key123' })`
|
|
- `AuthScheme.Basic({ username: 'user', password: 'pass' })`
|
|
- `AuthScheme.BearerToken({ token: 'token123' })`
|
|
|
|
**Use initiate() only when:**
|
|
- Building custom authentication UI
|
|
- Handling credentials directly in backend
|
|
- OAuth requires extra parameters before redirect
|
|
|
|
## Read Connected Accounts
|
|
|
|
```typescript
|
|
// List all
|
|
const allAccounts = await composio.connectedAccounts.list();
|
|
|
|
// Filter by user
|
|
const userAccounts = await composio.connectedAccounts.list({
|
|
userIds: ['user_123'],
|
|
});
|
|
|
|
// Filter by toolkit
|
|
const githubAccounts = await composio.connectedAccounts.list({
|
|
toolkitSlugs: ['github'],
|
|
});
|
|
|
|
// Filter by status
|
|
const activeAccounts = await composio.connectedAccounts.list({
|
|
statuses: ['ACTIVE']
|
|
});
|
|
|
|
// Filter by auth config
|
|
const configAccounts = await composio.connectedAccounts.list({
|
|
authConfigIds: ['auth_config_123']
|
|
});
|
|
|
|
// Combine filters
|
|
const filtered = await composio.connectedAccounts.list({
|
|
userIds: ['user_123'],
|
|
toolkitSlugs: ['github', 'slack'],
|
|
statuses: ['ACTIVE']
|
|
});
|
|
|
|
// Get specific account
|
|
const account = await composio.connectedAccounts.get('conn_abc123');
|
|
```
|
|
|
|
**Available filters:**
|
|
- `userIds` - Filter by user IDs
|
|
- `toolkitSlugs` - Filter by toolkit slugs
|
|
- `statuses` - Filter by connection statuses (see below for values)
|
|
- `authConfigIds` - Filter by auth config IDs
|
|
- `limit` - Results per page
|
|
- `cursor` - Pagination cursor
|
|
- `orderBy` - 'created_at' or 'updated_at'
|
|
|
|
## Update Connected Accounts
|
|
|
|
```typescript
|
|
// Enable/disable
|
|
await composio.connectedAccounts.enable('conn_abc123');
|
|
await composio.connectedAccounts.disable('conn_abc123');
|
|
|
|
// Refresh credentials (expired OAuth tokens)
|
|
await composio.connectedAccounts.refresh('conn_abc123');
|
|
```
|
|
|
|
## Delete Connected Accounts
|
|
|
|
```typescript
|
|
await composio.connectedAccounts.delete('conn_abc123');
|
|
```
|
|
|
|
**Warning:** Permanent deletion. User must re-authenticate.
|
|
|
|
## Wait for Connection Completion
|
|
|
|
For async OAuth flows:
|
|
|
|
```typescript
|
|
// Default timeout (60 seconds)
|
|
const account = await composio.connectedAccounts.waitForConnection('conn_123');
|
|
|
|
// Custom timeout (2 minutes)
|
|
const account = await composio.connectedAccounts.waitForConnection('conn_123', 120000);
|
|
```
|
|
|
|
**Errors:**
|
|
- `ComposioConnectedAccountNotFoundError` - Account doesn't exist
|
|
- `ConnectionRequestFailedError` - Connection failed/expired
|
|
- `ConnectionRequestTimeoutError` - Timeout exceeded
|
|
|
|
## Common Patterns
|
|
|
|
### OAuth Flow
|
|
|
|
```typescript
|
|
// Create connection
|
|
async function connectUser(userId, authConfigId) {
|
|
const request = await composio.connectedAccounts.link(
|
|
userId,
|
|
authConfigId,
|
|
{ callbackUrl: 'https://app.com/callback' }
|
|
);
|
|
return { redirectUrl: request.redirectUrl };
|
|
}
|
|
|
|
// Handle callback
|
|
async function handleCallback(connectionId) {
|
|
try {
|
|
const account = await composio.connectedAccounts.waitForConnection(
|
|
connectionId,
|
|
180000
|
|
);
|
|
return { success: true, account };
|
|
} catch (error) {
|
|
if (error.name === 'ConnectionRequestTimeoutError') {
|
|
return { error: 'Timeout. Please try again.' };
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Check Active Connections
|
|
|
|
```typescript
|
|
// Filter by status using statuses parameter
|
|
async function getUserActiveConnections(userId) {
|
|
const accounts = await composio.connectedAccounts.list({
|
|
userIds: [userId],
|
|
statuses: ['ACTIVE']
|
|
});
|
|
return accounts.items;
|
|
}
|
|
|
|
// Check multiple statuses
|
|
async function getUserConnectionsByStatus(userId) {
|
|
const accounts = await composio.connectedAccounts.list({
|
|
userIds: [userId],
|
|
statuses: ['ACTIVE', 'EXPIRED', 'FAILED']
|
|
});
|
|
return accounts.items;
|
|
}
|
|
|
|
async function isToolkitConnected(userId, toolkit) {
|
|
const accounts = await composio.connectedAccounts.list({
|
|
userIds: [userId],
|
|
toolkitSlugs: [toolkit],
|
|
statuses: ['ACTIVE']
|
|
});
|
|
return accounts.items.length > 0;
|
|
}
|
|
```
|
|
|
|
**Available statuses:**
|
|
- `INITIALIZING` - Connection being set up
|
|
- `INITIATED` - Connection initiated, awaiting completion
|
|
- `ACTIVE` - Connection active and ready to use
|
|
- `FAILED` - Connection failed
|
|
- `EXPIRED` - Credentials expired
|
|
- `INACTIVE` - Connection disabled
|
|
|
|
## Key Points
|
|
|
|
- **Prefer link()** - Security, UI, and whitelabeling handled
|
|
- **Store account IDs** - Save in your database, associate with users
|
|
- **Check status** - Verify ACTIVE before use, refresh on errors
|
|
- **Handle lifecycle** - Disable instead of delete when possible
|