Files
composio-skills-reference/composio-sdk/rules/tr-userid-best-practices.md
sohamganatra b8b711dff6 Add Composio SDK skill with rules and agent config
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>
2026-02-05 22:54:21 -08:00

477 lines
12 KiB
Markdown

---
title: Choose User IDs Carefully for Security and Isolation
impact: CRITICAL
description: Use proper user IDs to ensure data isolation, security, and correct session management
tags: [tool-router, user-id, security, authentication, multi-tenant]
---
# Choose User IDs Carefully for Security and Isolation
User IDs are the **foundation of Tool Router isolation**. They determine which user's connections, data, and permissions are used for tool execution. Choose them carefully to ensure security and proper data isolation.
## ❌ Incorrect
```typescript
// DON'T: Use 'default' in production multi-user apps
async function handleUserRequest(req: Request) {
const session = await composio.create('default', {
toolkits: ['gmail', 'slack']
});
// ❌ All users share the same session
// ❌ No data isolation
// ❌ Security nightmare
// ❌ User A can access User B's emails!
}
```
```python
# DON'T: Use 'default' in production multi-user apps
async def handle_user_request(req):
session = composio.tool_router.create(
user_id="default",
toolkits=["gmail", "slack"]
)
# ❌ All users share the same session
# ❌ No data isolation
# ❌ Security nightmare
# ❌ User A can access User B's emails!
```
```typescript
// DON'T: Use email addresses as user IDs
async function handleUserRequest(req: Request) {
const session = await composio.create(req.user.email, {
toolkits: ['github']
});
// ❌ Emails can change
// ❌ Breaks session continuity
// ❌ Historical data loss
}
```
## ✅ Correct - Use Database User IDs
```typescript
// DO: Use your database user ID (UUID, primary key, etc.)
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
provider: new VercelProvider()
});
async function handleUserRequest(req: Request) {
// Get user ID from your auth system
const userId = req.user.id; // e.g., "550e8400-e29b-41d4-a716-446655440000"
// Create isolated session for this user
const session = await composio.create(userId, {
toolkits: ['gmail', 'slack']
});
const tools = await session.tools();
// ✅ Each user gets their own session
// ✅ Complete data isolation
// ✅ User A cannot access User B's data
// ✅ Connections tied to correct user
return await agent.run(req.message, tools);
}
```
```python
# DO: Use your database user ID (UUID, primary key, etc.)
from composio import Composio
from composio_openai import OpenAIProvider
composio = Composio(provider=OpenAIProvider())
async def handle_user_request(req):
# Get user ID from your auth system
user_id = req.user.id # e.g., "550e8400-e29b-41d4-a716-446655440000"
# Create isolated session for this user
session = composio.tool_router.create(
user_id=user_id,
toolkits=["gmail", "slack"]
)
tools = session.tools()
# ✅ Each user gets their own session
# ✅ Complete data isolation
# ✅ User A cannot access User B's data
# ✅ Connections tied to correct user
return await agent.run(req.message, tools)
```
## ✅ Correct - Use Auth Provider IDs
```typescript
// DO: Use IDs from your auth provider
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
provider: new VercelProvider()
});
async function handleClerkUser(userId: string) {
// Using Clerk user ID
// e.g., "user_2abc123def456"
const session = await composio.create(userId, {
toolkits: ['github']
});
return session;
}
async function handleAuth0User(userId: string) {
// Using Auth0 user ID
// e.g., "auth0|507f1f77bcf86cd799439011"
const session = await composio.create(userId, {
toolkits: ['gmail']
});
return session;
}
async function handleSupabaseUser(userId: string) {
// Using Supabase user UUID
// e.g., "d7f8b0c1-1234-5678-9abc-def012345678"
const session = await composio.create(userId, {
toolkits: ['slack']
});
return session;
}
```
```python
# DO: Use IDs from your auth provider
from composio import Composio
from composio_openai import OpenAIProvider
composio = Composio(provider=OpenAIProvider())
async def handle_clerk_user(user_id: str):
# Using Clerk user ID
# e.g., "user_2abc123def456"
session = composio.tool_router.create(
user_id=user_id,
toolkits=["github"]
)
return session
async def handle_auth0_user(user_id: str):
# Using Auth0 user ID
# e.g., "auth0|507f1f77bcf86cd799439011"
session = composio.tool_router.create(
user_id=user_id,
toolkits=["gmail"]
)
return session
async def handle_supabase_user(user_id: str):
# Using Supabase user UUID
# e.g., "d7f8b0c1-1234-5678-9abc-def012345678"
session = composio.tool_router.create(
user_id=user_id,
toolkits=["slack"]
)
return session
```
## ✅ Correct - Organization-Level Applications
```typescript
// DO: Use organization ID for org-level apps
import { Composio } from '@composio/core';
import { VercelProvider } from '@composio/vercel';
const composio = new Composio({
provider: new VercelProvider()
});
// When apps are connected at organization level (not individual users)
async function handleOrgLevelApp(req: Request) {
// Use organization ID, NOT individual user ID
const organizationId = req.user.organizationId;
const session = await composio.create(organizationId, {
toolkits: ['slack', 'github'], // Org-wide tools
manageConnections: true
});
// All users in the organization share these connections
// Perfect for team collaboration tools
const tools = await session.tools();
return await agent.run(req.message, tools);
}
// Example: Slack workspace integration
async function createWorkspaceSession(workspaceId: string) {
// Workspace ID as user ID
const session = await composio.create(`workspace_${workspaceId}`, {
toolkits: ['slack', 'notion', 'linear']
});
return session;
}
```
```python
# DO: Use organization ID for org-level apps
from composio import Composio
from composio_openai import OpenAIProvider
composio = Composio(provider=OpenAIProvider())
# When apps are connected at organization level (not individual users)
async def handle_org_level_app(req):
# Use organization ID, NOT individual user ID
organization_id = req.user.organization_id
session = composio.tool_router.create(
user_id=organization_id,
toolkits=["slack", "github"], # Org-wide tools
manage_connections=True
)
# All users in the organization share these connections
# Perfect for team collaboration tools
tools = session.tools()
return await agent.run(req.message, tools)
# Example: Slack workspace integration
async def create_workspace_session(workspace_id: str):
# Workspace ID as user ID
session = composio.tool_router.create(
user_id=f"workspace_{workspace_id}",
toolkits=["slack", "notion", "linear"]
)
return session
```
## When to Use 'default'
The `'default'` user ID should **ONLY** be used in these scenarios:
### ✅ Development and Testing
```typescript
// Testing locally
const session = await composio.create('default', {
toolkits: ['gmail']
});
```
### ✅ Single-User Applications
```typescript
// Personal automation script
// Only YOU use this app
const session = await composio.create('default', {
toolkits: ['github', 'notion']
});
```
### ✅ Demos and Prototypes
```typescript
// Quick demo for investors
const session = await composio.create('default', {
toolkits: ['hackernews']
});
```
### ❌ NEVER in Production Multi-User Apps
```typescript
// Production API serving multiple users
// ❌ DON'T DO THIS
const session = await composio.create('default', {
toolkits: ['gmail']
});
```
## User ID Best Practices
### 1. **Use Stable, Immutable Identifiers**
**Good:**
- Database primary keys (UUIDs)
- Auth provider user IDs
- Immutable user identifiers
**Bad:**
- Email addresses (can change)
- Usernames (can be modified)
- Phone numbers (can change)
```typescript
// ✅ Good: Stable UUID
const userId = user.id; // "550e8400-e29b-41d4-a716-446655440000"
// ❌ Bad: Email (mutable)
const userId = user.email; // "john@example.com" -> changes to "john@newdomain.com"
// ❌ Bad: Username (mutable)
const userId = user.username; // "john_doe" -> changes to "john_smith"
```
### 2. **Ensure Uniqueness**
```typescript
// ✅ Good: Guaranteed unique
const userId = database.users.findById(id).id;
// ✅ Good: Auth provider guarantees uniqueness
const userId = auth0.user.sub; // "auth0|507f1f77bcf86cd799439011"
// ❌ Bad: Not guaranteed unique
const userId = user.firstName; // Multiple "John"s exist
```
### 3. **Match Your Authentication System**
```typescript
// Express.js with Passport
app.post('/api/agent', authenticateUser, async (req, res) => {
const userId = req.user.id; // From Passport
const session = await composio.create(userId, config);
});
// Next.js with Clerk
export async function POST(req: NextRequest) {
const { userId } = auth(); // From Clerk
const session = await composio.create(userId!, config);
}
// FastAPI with Auth0
@app.post("/api/agent")
async def agent_endpoint(user: User = Depends(get_current_user)):
user_id = user.id # From Auth0
session = composio.tool_router.create(user_id=user_id, **config)
```
### 4. **Namespace for Multi-Tenancy**
```typescript
// When you have multiple applications/workspaces per user
const userId = `app_${appId}_user_${user.id}`;
// e.g., "app_saas123_user_550e8400"
const session = await composio.create(userId, {
toolkits: ['gmail']
});
// Each app instance gets isolated connections
```
### 5. **Be Consistent Across Your Application**
```typescript
// ✅ Good: Same user ID everywhere
async function handleRequest(req: Request) {
const userId = req.user.id;
// Use same ID for Tool Router
const session = await composio.create(userId, config);
// Use same ID for direct tool execution
await composio.tools.execute('GMAIL_SEND_EMAIL', {
userId: userId,
arguments: { to: 'user@example.com', subject: 'Test' }
});
// Use same ID for connected accounts
await composio.connectedAccounts.get(userId, 'gmail');
}
```
## Security Implications
### ⚠️ User ID Leakage
```typescript
// ❌ DON'T: Expose user IDs to client
app.get('/api/session', (req, res) => {
res.json({
sessionId: session.sessionId,
userId: req.user.id // ❌ Sensitive information
});
});
// ✅ DO: Keep user IDs server-side only
app.get('/api/session', (req, res) => {
res.json({
sessionId: session.sessionId
// Don't send userId to client
});
});
```
### ⚠️ User ID Validation
```typescript
// ✅ Always validate user IDs match authenticated user
app.post('/api/agent/:userId', authenticateUser, async (req, res) => {
const requestedUserId = req.params.userId;
const authenticatedUserId = req.user.id;
// Validate user can only access their own data
if (requestedUserId !== authenticatedUserId) {
return res.status(403).json({ error: 'Forbidden' });
}
const session = await composio.create(authenticatedUserId, config);
});
```
## Common Patterns
### Pattern 1: User-Level Isolation (Most Common)
```typescript
// Each user has their own connections
// Use user ID from your database/auth system
const session = await composio.create(req.user.id, {
toolkits: ['gmail', 'github']
});
```
### Pattern 2: Organization-Level Sharing
```typescript
// All org members share connections
// Use organization ID
const session = await composio.create(req.user.organizationId, {
toolkits: ['slack', 'notion']
});
```
### Pattern 3: Hybrid (User + Org)
```typescript
// Personal tools use user ID
const personalSession = await composio.create(req.user.id, {
toolkits: ['gmail'] // Personal Gmail
});
// Team tools use org ID
const teamSession = await composio.create(req.user.organizationId, {
toolkits: ['slack', 'jira'] // Team Slack/Jira
});
```
## Key Principles
1. **Never use 'default' in production multi-user apps**
2. **Use stable, immutable identifiers** (UUIDs, not emails)
3. **Match your authentication system's user IDs**
4. **Validate user IDs server-side** for security
5. **Be consistent** across Tool Router and direct tool usage
6. **Use org IDs** for organization-level applications
7. **Namespace when needed** for multi-tenancy
## Reference
- [Tool Router Sessions](https://docs.composio.dev/sdk/typescript/api/tool-router#creating-sessions)
- [User ID Security](https://docs.composio.dev/sdk/typescript/core-concepts#user-ids)
- [Connected Accounts](https://docs.composio.dev/sdk/typescript/api/connected-accounts)