#!/usr/bin/env node
import dotenv from 'dotenv';
import { UserSessionManager } from './user-session-manager.js';
dotenv.config();
async function demonstrateMultiTenantFunctionality() {
console.log('π§ͺ MULTI-TENANT AUTHENTICATION DEMO');
console.log('=====================================');
console.log(`π
Demo Time: ${new Date().toISOString()}\n`);
const baseURL = process.env.UMBRELLA_API_BASE_URL || 'https://api.umbrellacost.io/api/v1';
const sessionManager = new UserSessionManager(baseURL);
// Test credentials from .env
const user1 = {
username: process.env.UMBRELLA_TEST_MSP_USERNAME || 'elisha@umbrellacost.cloud',
password: process.env.UMBRELLA_TEST_MSP_PASSWORD || '6K2UX6DoYSgV%E'
};
const user2 = {
username: process.env.UMBRELLA_TEST_DIRECT_USERNAME || 'elisha@umbrellacost.net',
password: process.env.UMBRELLA_TEST_DIRECT_PASSWORD || 'G37oi57Kp@cNzx'
};
console.log('π₯ TESTING MULTI-USER AUTHENTICATION');
console.log('=====================================\n');
// Test 1: Authenticate first user
console.log(`1οΈβ£ Authenticating User 1: ${user1.username}`);
const auth1 = await sessionManager.authenticateUser(user1);
console.log(` Result: ${auth1.success ? 'β
Success' : 'β Failed'}`);
if (auth1.success) {
console.log(` Session ID: ${auth1.sessionId}`);
} else {
console.log(` Error: ${auth1.error}`);
}
// Test 2: Authenticate second user
console.log(`\n2οΈβ£ Authenticating User 2: ${user2.username}`);
const auth2 = await sessionManager.authenticateUser(user2);
console.log(` Result: ${auth2.success ? 'β
Success' : 'β Failed'}`);
if (auth2.success) {
console.log(` Session ID: ${auth2.sessionId}`);
} else {
console.log(` Error: ${auth2.error}`);
}
// Test 3: Check session status
console.log(`\n3οΈβ£ Checking Active Sessions`);
const activeSessions = sessionManager.getActiveSessions();
console.log(` Active Sessions: ${activeSessions.length}`);
activeSessions.forEach((session, index) => {
console.log(` ${index + 1}. ${session.username} - Last active: ${session.lastActivity.toISOString()}`);
});
// Test 4: Test API calls with different users
console.log(`\n4οΈβ£ Testing API Calls with Different Users`);
if (auth1.success) {
console.log(`\n π User 1 (${user1.username}) - Service Names API:`);
const session1 = sessionManager.getUserSessionByUsername(user1.username);
if (session1) {
try {
const response1 = await session1.apiClient.makeRequest('/invoices/service-names/distinct', { limit: 5 });
if (response1.success) {
console.log(` β
Success: ${Array.isArray(response1.data) ? response1.data.length : 'data'} items`);
if (Array.isArray(response1.data) && response1.data.length > 0) {
console.log(` π Sample: ${response1.data[0]}`);
}
} else {
console.log(` β Failed: ${response1.error}`);
}
} catch (error: any) {
console.log(` π₯ Error: ${error.message}`);
}
}
}
if (auth2.success) {
console.log(`\n π User 2 (${user2.username}) - Recommendations API:`);
const session2 = sessionManager.getUserSessionByUsername(user2.username);
if (session2) {
try {
const response2 = await session2.apiClient.makeRequest('/recommendations/report');
if (response2.success) {
console.log(` β
Success: ${Array.isArray(response2.data) ? response2.data.length : 'data'} items`);
if (Array.isArray(response2.data) && response2.data.length > 0) {
console.log(` π Report: ${response2.data[0].reportName || 'Unnamed'}`);
}
} else {
console.log(` β Failed: ${response2.error}`);
}
} catch (error: any) {
console.log(` π₯ Error: ${error.message}`);
}
}
}
// Test 5: Session statistics
console.log(`\n5οΈβ£ Session Statistics`);
const stats = sessionManager.getStats();
console.log(` Total Sessions: ${stats.totalSessions}`);
console.log(` Active Sessions: ${stats.activeSessions}`);
console.log(` Expired Sessions: ${stats.expiredSessions}`);
// Test 6: Logout specific user
console.log(`\n6οΈβ£ Testing Logout Functionality`);
if (auth1.success) {
console.log(` Logging out User 1: ${user1.username}`);
const loggedOut = sessionManager.removeUserSession(user1.username);
console.log(` Result: ${loggedOut ? 'β
Success' : 'β Failed'}`);
const updatedSessions = sessionManager.getActiveSessions();
console.log(` Remaining Active Sessions: ${updatedSessions.length}`);
}
// Test 7: Try to use logged out session
console.log(`\n7οΈβ£ Testing Access After Logout`);
const loggedOutSession = sessionManager.getUserSessionByUsername(user1.username);
console.log(` User 1 Session After Logout: ${loggedOutSession ? 'β Still exists' : 'β
Properly removed'}`);
// Test 8: Session cleanup simulation
console.log(`\n8οΈβ£ Testing Session Timeout (Simulated)`);
if (auth2.success) {
const session2 = sessionManager.getUserSessionByUsername(user2.username);
if (session2) {
// Simulate old activity
session2.lastActivity = new Date(Date.now() - 35 * 60 * 1000); // 35 minutes ago
console.log(` Simulated User 2 inactive for 35 minutes`);
// Try to get session (should fail due to timeout)
const expiredSession = sessionManager.getUserSession(session2.id);
console.log(` Session retrieval after timeout: ${expiredSession ? 'β Should have expired' : 'β
Properly expired'}`);
}
}
// Cleanup
console.log(`\n9οΈβ£ Final Cleanup`);
sessionManager.shutdown();
console.log(` β
Session manager shutdown complete`);
console.log(`\n${'='.repeat(50)}`);
console.log('π MULTI-TENANT DEMO COMPLETED SUCCESSFULLY!');
console.log(`${'='.repeat(50)}`);
console.log('\nπ‘ KEY FEATURES DEMONSTRATED:');
console.log(' β
Multiple users can authenticate simultaneously');
console.log(' β
Each user has their own isolated session');
console.log(' β
API calls are properly scoped to individual users');
console.log(' β
Session management (create, retrieve, remove)');
console.log(' β
Automatic session timeout and cleanup');
console.log(' β
Graceful shutdown and resource cleanup');
console.log('\nπ PRODUCTION BENEFITS:');
console.log(' π Multiple Claude Desktop users can share one MCP server');
console.log(' π Each user sees only their own Umbrella Cost data');
console.log(' β‘ Efficient resource usage with shared server infrastructure');
console.log(' π‘οΈ Automatic security through session isolation');
console.log(' π§Ή Built-in session cleanup prevents memory leaks');
}
// Run the demo
demonstrateMultiTenantFunctionality().catch(console.error);