Skip to main content
Glama
explain-umbrella-auth.cjs13.1 kB
#!/usr/bin/env node /** * UMBRELLA API AUTHENTICATION EXPLANATION * ======================================== * This script explains and demonstrates how Umbrella API authentication works */ const axios = require('axios'); const crypto = require('crypto'); // Color codes for terminal output const colors = { green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', magenta: '\x1b[35m', reset: '\x1b[0m', bold: '\x1b[1m', underline: '\x1b[4m' }; console.log(` ${colors.cyan}${colors.bold}════════════════════════════════════════════════════════════════════════ UMBRELLA API AUTHENTICATION SYSTEM ════════════════════════════════════════════════════════════════════════${colors.reset} `); console.log(`${colors.bold}${colors.underline}HOW UMBRELLA API AUTHENTICATION WORKS:${colors.reset} `); // STEP 1: Initial Authentication console.log(`${colors.green}${colors.bold}STEP 1: INITIAL AUTHENTICATION${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} The authentication process starts by sending credentials to the ${colors.yellow}/authentication${colors.reset} endpoint: ${colors.magenta}POST https://api.umbrellacost.io/api/v1/authentication${colors.reset} ${colors.blue}Body:${colors.reset} { "username": "user@example.com", "password": "password123" } ${colors.green}Response:${colors.reset} { "access_token": "Bearer xxx.yyy.zzz", "refresh_token": "refresh_xxx", "expires_in": 3600 } `); // STEP 2: Dual-Header System console.log(`${colors.green}${colors.bold}STEP 2: DUAL-HEADER AUTHENTICATION SYSTEM${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} Umbrella API uses a ${colors.bold}DUAL-HEADER${colors.reset} authentication system: ${colors.yellow}1. Authorization Header:${colors.reset} • Contains the Bearer token from authentication • Format: "Bearer <access_token>" • This identifies WHO you are ${colors.yellow}2. apikey Header:${colors.reset} • Contains a context-specific API key • This determines WHAT DATA you can access • Has a hierarchy system (see Step 3) ${colors.blue}Example headers for API call:${colors.reset} { "Authorization": "Bearer xxx.yyy.zzz", "apikey": "context-specific-key-here" } `); // STEP 3: API Key Hierarchy console.log(`${colors.green}${colors.bold}STEP 3: API KEY HIERARCHY${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} The ${colors.yellow}apikey${colors.reset} header follows a priority hierarchy: ${colors.bold}Priority Order (High → Low):${colors.reset} ${colors.green}1. CUSTOMER-SPECIFIC KEY${colors.reset} (Highest Priority) • Used when accessing specific customer data (MSP scenarios) • Built dynamically using customer account key • Example: "customer_12345_encrypted_key" • Provides access to that customer's data only ${colors.blue}2. CLOUD-CONTEXT KEY${colors.reset} (Medium Priority) • Used when accessing cloud-specific data (AWS, Azure, GCP) • Built based on cloud provider context • Example: "aws_context_key" or "azure_context_key" • Limits data to specific cloud provider ${colors.yellow}3. DEFAULT KEY${colors.reset} (Lowest Priority) • The base API key from authentication • Used when no specific context is needed • Provides general access to your account data `); // STEP 4: How Keys Are Built console.log(`${colors.green}${colors.bold}STEP 4: HOW CONTEXT-SPECIFIC KEYS ARE BUILT${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} Context-specific keys are built dynamically: ${colors.yellow}For Customer-Specific Access:${colors.reset} 1. Take the customer's account key 2. Encrypt it with AES-256-GCM 3. Encode with Base64 4. Format: "customer_<encrypted_data>" ${colors.yellow}For Cloud-Specific Access:${colors.reset} 1. Take the cloud provider name (aws/azure/gcp) 2. Append to base token 3. Format: "<base_token>_<cloud>" ${colors.blue}Code example from api-client.ts:${colors.reset} `); console.log(`${colors.magenta} // Priority logic in getAuthHeaders(): let apiKey = null; // 1. Try customer-specific key first if (customerAccountKey) { apiKey = await buildCustomerApiKey(customerAccountKey); } // 2. Try cloud-specific key if no customer key if (!apiKey && cloudContext) { apiKey = buildCloudContextApiKey(cloudContext); } // 3. Use default key as fallback if (!apiKey) { apiKey = this._authToken.apikey; } headers['apikey'] = apiKey; ${colors.reset}`); // STEP 5: Making API Calls console.log(`${colors.green}${colors.bold}STEP 5: MAKING AUTHENTICATED API CALLS${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} Every API call to Umbrella requires both headers: ${colors.yellow}Example: Getting AWS costs for a specific month:${colors.reset} ${colors.magenta}GET https://api.umbrellacost.io/api/v1/invoices/caui${colors.reset} ${colors.blue}Headers:${colors.reset} { "Authorization": "Bearer xxx.yyy.zzz", "apikey": "aws_context_key" } ${colors.blue}Query Parameters:${colors.reset} { "startDate": "2025-01-01", "endDate": "2025-01-31", "cloudProvider": "aws", "periodGranLevel": "month" } `); // STEP 6: MSP Customer Access console.log(`${colors.green}${colors.bold}STEP 6: MSP (MANAGED SERVICE PROVIDER) ACCESS${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} For MSP scenarios (accessing multiple customer accounts): ${colors.yellow}1. Initial Authentication:${colors.reset} • MSP authenticates with their credentials • Receives token with access to all managed customers ${colors.yellow}2. Customer List Retrieval:${colors.reset} • Call /users/plain-sub-users to get customer divisions • Returns list of customers with their account keys ${colors.yellow}3. Customer-Specific Access:${colors.reset} • When accessing customer data, build customer-specific apikey • Add 'commonparams' header: {"isPpApplied": true} • This ensures correct data isolation ${colors.blue}Example MSP API call:${colors.reset} { "Authorization": "Bearer msp_token", "apikey": "encrypted_customer_key", "commonparams": "{\\"isPpApplied\\":true}" } `); // STEP 7: Token Lifecycle console.log(`${colors.green}${colors.bold}STEP 7: TOKEN LIFECYCLE & REFRESH${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} ${colors.yellow}Token Expiration:${colors.reset} • Access tokens typically expire in 1 hour (3600 seconds) • Must be refreshed before expiration ${colors.yellow}Refresh Process:${colors.reset} 1. Use refresh_token to get new access_token 2. Update Authorization header with new token 3. Continue making API calls ${colors.yellow}Session State:${colors.reset} • Authentication is stateless - each request is independent • Token contains all necessary session information • No server-side session storage required `); // STEP 8: Error Handling console.log(`${colors.green}${colors.bold}STEP 8: AUTHENTICATION ERROR HANDLING${colors.reset} ${colors.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset} Common authentication errors and their meanings: ${colors.red}401 Unauthorized:${colors.reset} • Invalid or expired token • Solution: Re-authenticate or refresh token ${colors.red}403 Forbidden:${colors.reset} • Valid authentication but no access to resource • Wrong apikey for the requested data • Solution: Check context and permissions ${colors.red}"Authentication failed" message:${colors.reset} • Invalid username/password • Account may be locked or disabled • Solution: Verify credentials `); // Practical Example console.log(` ${colors.cyan}${colors.bold}════════════════════════════════════════════════════════════════════════ PRACTICAL EXAMPLE ════════════════════════════════════════════════════════════════════════${colors.reset} `); // Demo authentication flow async function demonstrateAuth() { console.log(`${colors.yellow}Let's demonstrate the authentication flow:${colors.reset}\n`); // Mock credentials (replace with real ones for testing) const credentials = { username: 'david+saola@umbrellacost.com', password: 'Dsamsung1!' }; console.log(`${colors.blue}1. Authenticating with Umbrella API...${colors.reset}`); try { const authResponse = await axios.post( 'https://api.umbrellacost.io/api/v1/authentication', credentials ); if (authResponse.data && authResponse.data.access_token) { console.log(`${colors.green}✅ Authentication successful!${colors.reset}`); console.log(` Token: ${authResponse.data.access_token.substring(0, 50)}...`); // Now make an API call with the token console.log(`\n${colors.blue}2. Making authenticated API call to get costs...${colors.reset}`); const headers = { 'Authorization': `Bearer ${authResponse.data.access_token}`, 'apikey': authResponse.data.access_token }; const costResponse = await axios.get( 'https://api.umbrellacost.io/api/v1/invoices/caui', { headers: headers, params: { startDate: '2025-01-01', endDate: '2025-01-31', periodGranLevel: 'month' } } ); console.log(`${colors.green}✅ API call successful!${colors.reset}`); console.log(` Response has ${Object.keys(costResponse.data).length} fields`); } } catch (error) { if (error.response?.status === 401) { console.log(`${colors.red}❌ Authentication failed (401)${colors.reset}`); console.log(` This means the credentials are invalid`); } else if (error.response?.status === 403) { console.log(`${colors.red}❌ Access forbidden (403)${colors.reset}`); console.log(` You're authenticated but don't have permission`); } else { console.log(`${colors.red}❌ Error: ${error.message}${colors.reset}`); } } } // SUMMARY console.log(` ${colors.cyan}${colors.bold}════════════════════════════════════════════════════════════════════════ KEY POINTS SUMMARY ════════════════════════════════════════════════════════════════════════${colors.reset} ${colors.green}${colors.bold}Key Takeaways:${colors.reset} ${colors.yellow}1.${colors.reset} Umbrella uses ${colors.bold}dual-header authentication${colors.reset} (Authorization + apikey) ${colors.yellow}2.${colors.reset} The ${colors.bold}Authorization header${colors.reset} identifies WHO you are ${colors.yellow}3.${colors.reset} The ${colors.bold}apikey header${colors.reset} determines WHAT DATA you can access ${colors.yellow}4.${colors.reset} API keys follow a ${colors.bold}hierarchy${colors.reset}: Customer > Cloud > Default ${colors.yellow}5.${colors.reset} MSP scenarios use ${colors.bold}customer-specific keys${colors.reset} for data isolation ${colors.yellow}6.${colors.reset} All requests are ${colors.bold}stateless${colors.reset} - token contains all session info ${colors.yellow}7.${colors.reset} Tokens expire and must be ${colors.bold}refreshed${colors.reset} periodically `); // Run demonstration if requested if (process.argv.includes('--demo')) { console.log(`\n${colors.cyan}${colors.bold}Running live demonstration...${colors.reset}\n`); demonstrateAuth(); } else { console.log(`\n${colors.cyan}Run with --demo flag to see a live authentication example${colors.reset}`); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/daviddraiumbrella/invoice-monitoring'

If you have feedback or need assistance with the MCP directory API, please join our Discord server