use composio_sdk::ComposioClient;
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = env::var("COMPOSIO_API_KEY")
.expect("COMPOSIO_API_KEY environment variable must be set");
println!("╔════════════════════════════════════════════════════════════╗");
println!("║ Composio Authentication Workflow Example ║");
println!("╚════════════════════════════════════════════════════════════╝\n");
println!("🔧 Initializing Composio client...");
let client = ComposioClient::builder().api_key(api_key).build()?;
println!("✓ Client initialized\n");
let user_id = "user_demo_auth_123";
println!("👤 Creating session for user: {}", user_id);
println!(" Enabling toolkits: github, gmail, slack, notion");
let session = client
.create_session(user_id)
.toolkits(vec!["github", "gmail", "slack", "notion"])
.manage_connections(true) .send()
.await?;
println!("✓ Session created successfully!");
println!(" Session ID: {}", session.session_id());
println!(" MCP URL: {}\n", session.mcp_url());
println!("📋 Listing available toolkits and checking connection status...\n");
let toolkits = session.list_toolkits().limit(20).send().await?;
println!("Found {} toolkits (showing top {})", toolkits.total_items, toolkits.items.len());
println!("─────────────────────────────────────────────────────────────");
let mut connected_toolkits = Vec::new();
let mut disconnected_toolkits = Vec::new();
for toolkit in &toolkits.items {
let is_connected = toolkit.connected_account.is_some();
if is_connected {
connected_toolkits.push(toolkit);
println!("✓ {} ({})", toolkit.name, toolkit.slug);
println!(" Status: CONNECTED");
if let Some(account) = &toolkit.connected_account {
println!(" Account ID: {}", account.id);
println!(" Account Status: {}", account.status);
println!(" Created: {}", account.created_at);
}
} else {
disconnected_toolkits.push(toolkit);
println!("✗ {} ({})", toolkit.name, toolkit.slug);
println!(" Status: NOT CONNECTED");
println!(" Auth Required: {}", !toolkit.is_no_auth);
}
println!(" Tools: {} | Triggers: {}",
toolkit.meta.tools_count,
toolkit.meta.triggers_count);
println!();
}
println!("─────────────────────────────────────────────────────────────");
println!("📊 Connection Summary:");
println!(" Connected: {} toolkits", connected_toolkits.len());
println!(" Disconnected: {} toolkits", disconnected_toolkits.len());
println!();
println!("🔍 Checking specific toolkit connection status...\n");
let toolkits_to_check = vec!["github", "gmail", "slack"];
for toolkit_slug in &toolkits_to_check {
let result = session
.list_toolkits()
.toolkits(vec![*toolkit_slug])
.send()
.await?;
if let Some(toolkit) = result.items.first() {
let is_connected = toolkit.connected_account.is_some();
if is_connected {
println!("✓ {} is CONNECTED", toolkit.name);
if let Some(account) = &toolkit.connected_account {
println!(" Account: {} ({})", account.id, account.status);
}
} else {
println!("✗ {} is NOT CONNECTED", toolkit.name);
println!(" Authentication required before using tools");
}
} else {
println!("⚠ {} not found in session", toolkit_slug);
}
println!();
}
println!("🔗 Creating authentication links for disconnected toolkits...\n");
let callback_url = "https://your-app.com/auth/callback";
for toolkit in disconnected_toolkits.iter().take(3) {
println!("Creating auth link for {}...", toolkit.name);
match session
.create_auth_link(&toolkit.slug, Some(callback_url.to_string()))
.await
{
Ok(link) => {
println!("✓ Auth link created successfully!");
println!(" Link Token: {}", link.link_token);
println!(" Redirect URL: {}", link.redirect_url);
println!(" Callback URL: {}", callback_url);
if let Some(account_id) = link.connected_account_id {
println!(" Existing Account: {}", account_id);
} else {
println!(" New Connection: Yes");
}
println!("\n 📱 Send this URL to the user:");
println!(" {}", link.redirect_url);
println!();
}
Err(e) => {
println!("✗ Failed to create auth link: {}", e);
println!();
}
}
}
println!("─────────────────────────────────────────────────────────────");
println!("📖 Authentication Flow Explanation:\n");
println!("1️⃣ CHECK CONNECTION STATUS");
println!(" Use session.list_toolkits() to see which toolkits are connected");
println!(" Filter by is_connected(true/false) to get specific status\n");
println!("2️⃣ CREATE AUTH LINK");
println!(" For disconnected toolkits, call session.create_auth_link()");
println!(" Provide optional callback_url for redirect after auth\n");
println!("3️⃣ USER AUTHENTICATES");
println!(" Send redirect_url to user (via chat, email, or UI)");
println!(" User visits URL and completes OAuth flow with the service\n");
println!("4️⃣ HANDLE CALLBACK");
println!(" After authentication, user is redirected to callback_url");
println!(" Callback includes query parameters:");
println!(" - status: 'success' or 'failed'");
println!(" - connected_account_id: ID of the connected account\n");
println!("5️⃣ VERIFY CONNECTION");
println!(" Check connection status again using session.list_toolkits()");
println!(" Verify connected_account is present and status is 'ACTIVE'\n");
println!("6️⃣ EXECUTE TOOLS");
println!(" Once connected, you can execute tools for that toolkit");
println!(" Tools automatically use the user's authenticated credentials\n");
println!("─────────────────────────────────────────────────────────────");
println!("💡 Best Practices:\n");
println!("✓ Always check connection status before executing tools");
println!("✓ Use callback URLs in production for better user experience");
println!("✓ Store link_token for tracking authentication sessions");
println!("✓ Handle authentication errors gracefully");
println!("✓ Use in-chat auth (manage_connections=true) for conversational flows");
println!("✓ Use manual auth for onboarding or settings pages");
println!("✓ Check account status (ACTIVE, EXPIRED, FAILED) before tool execution");
println!("✓ Implement re-authentication flow for expired connections\n");
println!("─────────────────────────────────────────────────────────────");
println!("🔎 Advanced Filtering Examples:\n");
println!("Fetching only CONNECTED toolkits...");
let connected = session
.list_toolkits()
.is_connected(true)
.limit(5)
.send()
.await?;
println!("✓ Found {} connected toolkits", connected.items.len());
for toolkit in &connected.items {
println!(" - {} ({})", toolkit.name, toolkit.slug);
}
println!();
println!("Fetching only DISCONNECTED toolkits...");
let disconnected = session
.list_toolkits()
.is_connected(false)
.limit(5)
.send()
.await?;
println!("✓ Found {} disconnected toolkits", disconnected.items.len());
for toolkit in &disconnected.items {
println!(" - {} ({})", toolkit.name, toolkit.slug);
}
println!();
println!("Searching for 'mail' toolkits...");
let search_results = session
.list_toolkits()
.search("mail")
.limit(5)
.send()
.await?;
println!("✓ Found {} toolkits matching 'mail'", search_results.items.len());
for toolkit in &search_results.items {
let status = if toolkit.connected_account.is_some() {
"CONNECTED"
} else {
"NOT CONNECTED"
};
println!(" - {} ({}) - {}", toolkit.name, toolkit.slug, status);
}
println!();
println!("─────────────────────────────────────────────────────────────");
println!("✅ Authentication workflow example completed successfully!");
println!("─────────────────────────────────────────────────────────────\n");
Ok(())
}