UNPKG

@aashari/mcp-server-aws-sso

Version:

Node.js/TypeScript MCP server for AWS Single Sign-On (SSO). Enables AI systems (LLMs) with tools to initiate SSO login (device auth flow), list accounts/roles, and securely execute AWS CLI commands using temporary credentials. Streamlines AI interaction w

414 lines (413 loc) 16.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.getCachedSsoToken = getCachedSsoToken; exports.saveSsoToken = saveSsoToken; exports.getCachedCredentials = getCachedCredentials; exports.saveCachedCredentials = saveCachedCredentials; exports.cacheDeviceAuthorizationInfo = cacheDeviceAuthorizationInfo; exports.getCachedDeviceAuthorizationInfo = getCachedDeviceAuthorizationInfo; exports.clearDeviceAuthorizationInfo = clearDeviceAuthorizationInfo; exports.getCachedAccountRoles = getCachedAccountRoles; exports.saveAccountRoles = saveAccountRoles; exports.clearSsoToken = clearSsoToken; const fs = __importStar(require("fs/promises")); const fsSync = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const logger_util_js_1 = require("./logger.util.js"); const constants_util_js_1 = require("./constants.util.js"); const vendor_aws_sso_types_js_1 = require("../services/vendor.aws.sso.types.js"); const zod_1 = require("zod"); // Define the cache directory for MCP server const HOME_DIR = os.homedir(); const CACHE_DIR = path.join(HOME_DIR, '.mcp-server', constants_util_js_1.CLI_NAME); const TOKEN_FILE = path.join(CACHE_DIR, 'token.json'); /** * Ensure the cache directory exists */ async function ensureCacheDir() { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'ensureCacheDir'); methodLogger.debug('Ensuring cache directory exists'); try { // Make sure the cache directory exists if (!fsSync.existsSync(CACHE_DIR)) { methodLogger.debug(`Cache directory ${CACHE_DIR} does not exist, creating...`); fsSync.mkdirSync(CACHE_DIR, { recursive: true }); methodLogger.debug(`Cache directory created: ${CACHE_DIR}`); } } catch (error) { methodLogger.error('Error ensuring cache directory exists', error); throw error; } } /** * Check if a file exists * @param filePath File path * @returns True if the file exists, false otherwise */ async function fileExists(filePath) { try { await fs.access(filePath, fsSync.constants.F_OK); return true; } catch { return false; } } /** * Get cached SSO token * @returns Cached SSO token or undefined if not found or expired */ async function getCachedSsoToken() { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'getCachedSsoToken'); methodLogger.debug('Getting cached SSO token'); try { // Check if token file exists if (!(await fileExists(TOKEN_FILE))) { methodLogger.debug('No token file found'); return undefined; } // Read token from file const tokenContent = await fs.readFile(TOKEN_FILE, 'utf8'); try { // Parse and validate the token with Zod const token = vendor_aws_sso_types_js_1.SsoTokenSchema.parse(JSON.parse(tokenContent)); // Check if token is expired const now = Math.floor(Date.now() / 1000); if (token.expiresAt <= now) { methodLogger.debug('Token is expired'); return undefined; } // Token is valid methodLogger.debug('Found valid token, expires in', { expiresIn: token.expiresAt - now, expiresAt: new Date(token.expiresAt * 1000).toISOString(), }); return token; } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Token validation failed', error); } else { methodLogger.error('Error parsing token from cache', error); } return undefined; } } catch (error) { methodLogger.error('Error getting token from cache', error); return undefined; } } /** * Save SSO token to cache * @param token SSO token to save */ async function saveSsoToken(token) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'saveSsoToken'); methodLogger.debug('Saving SSO token to cache'); try { // Ensure cache directory exists await ensureCacheDir(); // Debug log - only log part of token for security methodLogger.debug('Token details before saving:', { accessTokenLength: token.accessToken?.length || 0, accessTokenFirst10Chars: token.accessToken?.substring(0, 10) || 'none', expiresAt: token.expiresAt, region: token.region, }); // Validate token with Zod schema before saving const validatedToken = vendor_aws_sso_types_js_1.SsoTokenSchema.parse(token); // Save token to file await fs.writeFile(TOKEN_FILE, JSON.stringify(validatedToken, null, 2), 'utf8'); methodLogger.debug('Token saved to cache'); } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Token validation failed', error); } else { methodLogger.error('Error saving token to cache', error); } throw error; } } /** * Get cached AWS credentials for account and role * @param accountId AWS account ID * @param roleName AWS role name * @returns AWS credentials or undefined if not found */ async function getCachedCredentials(accountId, roleName) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'getCachedCredentials'); methodLogger.debug('Getting cached credentials', { accountId, roleName }); try { // Generate credentials file path const key = `${accountId}_${roleName}`; const credentialsFile = path.join(CACHE_DIR, `${key}.json`); // Check if credentials file exists if (!(await fileExists(credentialsFile))) { methodLogger.debug('No credentials file found'); return undefined; } // Read credentials file const data = await fs.readFile(credentialsFile, 'utf8'); try { // Parse and validate with Zod schema const credentials = vendor_aws_sso_types_js_1.AwsCredentialsSchema.parse(JSON.parse(data)); methodLogger.debug('Retrieved credentials from cache', { accountId, roleName, expiration: credentials.expiration, }); return credentials; } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Credentials validation failed', error); } else { methodLogger.error('Error parsing credentials from cache', error); } return undefined; } } catch (error) { methodLogger.error('Error getting cached credentials', error); return undefined; } } /** * Save AWS credentials to cache * @param accountId AWS account ID * @param roleName AWS role name * @param credentials AWS credentials to save */ async function saveCachedCredentials(accountId, roleName, credentials) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'saveCachedCredentials'); methodLogger.debug('Saving credentials to cache', { accountId, roleName }); try { // Ensure cache directory exists await ensureCacheDir(); // Validate credentials with Zod schema const validatedCredentials = vendor_aws_sso_types_js_1.AwsCredentialsSchema.parse(credentials); // Generate credentials file path const key = `${accountId}_${roleName}`; const credentialsFile = path.join(CACHE_DIR, `${key}.json`); // Save credentials to file await fs.writeFile(credentialsFile, JSON.stringify(validatedCredentials, null, 2), 'utf8'); methodLogger.debug('Credentials saved to cache'); } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Credentials validation failed', error); } else { methodLogger.error('Error saving credentials to cache', error); } throw error; } } /** * Cache device authorization info * @param info Device authorization info to cache */ async function cacheDeviceAuthorizationInfo(info) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'cacheDeviceAuthorizationInfo'); methodLogger.debug('Caching device authorization info'); try { // Ensure cache directory exists await ensureCacheDir(); // Validate device authorization info with Zod schema const validatedInfo = vendor_aws_sso_types_js_1.DeviceAuthorizationInfoSchema.parse(info); // Save device authorization info to file const deviceAuthFile = path.join(CACHE_DIR, 'device-auth.json'); await fs.writeFile(deviceAuthFile, JSON.stringify(validatedInfo, null, 2), 'utf8'); methodLogger.debug('Device authorization info cached'); } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Device authorization info validation failed', error); } else { methodLogger.error('Error caching device authorization info', error); } throw error; } } /** * Get cached device authorization info * @returns Device authorization info from cache or undefined if not found */ async function getCachedDeviceAuthorizationInfo() { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'getCachedDeviceAuthorizationInfo'); methodLogger.debug('Getting cached device authorization info'); try { const deviceAuthFile = path.join(CACHE_DIR, 'device-auth.json'); // Check if device auth file exists if (!(await fileExists(deviceAuthFile))) { methodLogger.debug('No device authorization info found in cache'); return undefined; } // Read device auth file const data = await fs.readFile(deviceAuthFile, 'utf8'); try { // Parse and validate with Zod schema const deviceInfo = vendor_aws_sso_types_js_1.DeviceAuthorizationInfoSchema.parse(JSON.parse(data)); methodLogger.debug('Retrieved device authorization info from cache'); return deviceInfo; } catch (error) { if (error instanceof zod_1.z.ZodError) { methodLogger.error('Device authorization info validation failed', error); } else { methodLogger.error('Error parsing device authorization info from cache', error); } return undefined; } } catch (error) { methodLogger.error('Error getting cached device authorization info', error); return undefined; } } /** * Clear device authorization info from cache * @returns Promise that resolves when the operation completes */ async function clearDeviceAuthorizationInfo() { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'clearDeviceAuthorizationInfo'); methodLogger.debug('Clearing device authorization info'); try { const deviceAuthFile = path.join(CACHE_DIR, 'device-auth.json'); // Check if device auth file exists if (await fileExists(deviceAuthFile)) { // Delete device auth file await fs.unlink(deviceAuthFile); methodLogger.debug('Device authorization info cleared from cache'); } else { methodLogger.debug('No device authorization info found to clear'); } } catch (error) { methodLogger.error('Error clearing device authorization info', error); // Don't throw error to ensure other operations can continue } } /** * Get cached roles for an AWS account * @param accountId AWS account ID * @returns List of roles or empty array if none found */ async function getCachedAccountRoles(accountId) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'getCachedAccountRoles'); methodLogger.debug('Getting cached account roles', { accountId }); try { // Generate roles file path const rolesFile = path.join(CACHE_DIR, `roles_${accountId}.json`); // Check if roles file exists if (!(await fileExists(rolesFile))) { methodLogger.debug('No roles file found for account', { accountId, }); return []; } // Read roles file const data = await fs.readFile(rolesFile, 'utf8'); const roles = JSON.parse(data); methodLogger.debug(`Retrieved ${roles.length} roles for account`, { accountId, }); return roles; } catch (error) { methodLogger.error('Error getting cached account roles', error); return []; } } /** * Save roles for an AWS account to cache * @param account AWS account * @param roles List of roles to save */ async function saveAccountRoles(account, roles) { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'saveAccountRoles'); methodLogger.debug(`Saving ${roles.length} roles for account`, { accountId: account.accountId, }); try { // Ensure cache directory exists await ensureCacheDir(); // Save roles to file const rolesFile = path.join(CACHE_DIR, `roles_${account.accountId}.json`); await fs.writeFile(rolesFile, JSON.stringify(roles, null, 2), 'utf8'); methodLogger.debug('Account roles saved to cache'); } catch (error) { methodLogger.error('Error saving account roles to cache', error); throw error; } } /** * Clear the cached SSO token */ async function clearSsoToken() { const methodLogger = logger_util_js_1.Logger.forContext('utils/aws.sso.cache.util.ts', 'clearSsoToken'); methodLogger.debug('Clearing cached SSO token'); try { // Check if token file exists if (await fileExists(TOKEN_FILE)) { // Delete the token file await fs.unlink(TOKEN_FILE); methodLogger.debug('SSO token cleared from cache'); } else { methodLogger.debug('No token file found to clear'); } } catch (error) { methodLogger.error('Error clearing SSO token', error); throw error; } } /** * Save data to the MCP AWS SSO cache file * @param data The data to save */