UNPKG

@172ai/containers-mcp-server

Version:

MCP server for 172.ai container management platform - enables AI assistants to manage containers, builds, and files with comprehensive workflow prompts

195 lines 7.22 kB
import axios from 'axios'; import { config } from './config.js'; import { ErrorHandler, ApiError } from './utils/errorHandler.js'; /** * Authentication manager for the MCP server */ export class AuthManager { constructor() { this.httpClient = axios.create({ baseURL: config.get('baseUrl'), timeout: config.getTimeoutConfig().request, headers: { 'Content-Type': 'application/json', 'User-Agent': '@172ai/containers-mcp-server/1.0.0' } }); // Add request interceptor for auth this.httpClient.interceptors.request.use(async (config) => { const token = await this.getValidToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => Promise.reject(error)); // Add response interceptor for error handling this.httpClient.interceptors.response.use((response) => response, (error) => { const processedError = ErrorHandler.processError(error, 'AuthManager'); ErrorHandler.logError(processedError); return Promise.reject(new ApiError(processedError.message, processedError.status, processedError.code, processedError.details)); }); } /** * Get a valid authentication token */ async getValidToken() { const authConfig = config.getAuthConfig(); // Debug logging for authentication if (config.isDevelopment()) { console.log('[AuthManager] Getting valid token...'); console.log('[AuthManager] API key available:', !!authConfig.apiKey); console.log('[AuthManager] OAuth token available:', !!authConfig.oauthToken); console.log('[AuthManager] Client credentials available:', !!(authConfig.clientId && authConfig.clientSecret)); } // Use API key if available (preferred method) if (authConfig.apiKey) { if (config.isDevelopment()) { console.log('[AuthManager] Using API key for authentication'); } return authConfig.apiKey; } // Use existing OAuth token if valid if (authConfig.oauthToken && this.isTokenValid()) { return this.currentToken || authConfig.oauthToken; } // Get new OAuth token using client credentials if (authConfig.clientId && authConfig.clientSecret) { try { const token = await this.getOAuthToken(authConfig.clientId, authConfig.clientSecret); return token; } catch (error) { throw ErrorHandler.createAuthError('Failed to obtain OAuth token', { error: error.message }); } } throw ErrorHandler.createAuthError('No valid authentication method configured'); } /** * Get OAuth token using client credentials flow */ async getOAuthToken(clientId, clientSecret) { try { const response = await axios.post(`${config.get('baseUrl')}/oauth/token`, { grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret, scope: 'containers:read containers:write builds:read builds:write files:read files:write' }, { headers: { 'Content-Type': 'application/json' }, timeout: config.getTimeoutConfig().request }); const tokenData = response.data; this.currentToken = tokenData.access_token; this.tokenExpiry = new Date(Date.now() + (tokenData.expires_in * 1000)); return tokenData.access_token; } catch (error) { throw ErrorHandler.createAuthError('OAuth token request failed', { clientId, error: error.response?.data || error.message }); } } /** * Check if current token is valid and not expired */ isTokenValid() { if (!this.currentToken || !this.tokenExpiry) { return false; } // Check if token expires within next 5 minutes const fiveMinutesFromNow = new Date(Date.now() + 5 * 60 * 1000); return this.tokenExpiry > fiveMinutesFromNow; } /** * Get authenticated HTTP client */ getHttpClient() { return this.httpClient; } /** * Test authentication by making a simple API call */ async testAuthentication() { try { const response = await this.httpClient.get('/v1/containers?limit=1'); return response.status === 200; } catch (error) { if (error instanceof ApiError && (error.status === 401 || error.status === 403)) { return false; } throw error; } } /** * Get current user's API keys (if using OAuth) */ async getApiKeys() { try { const response = await this.httpClient.get('/v1/users/me/api-keys'); return response.data; } catch (error) { throw ErrorHandler.createAuthError('Failed to retrieve API keys', { error: error.message }); } } /** * Validate API key */ async validateApiKey(apiKey) { try { const response = await axios.get(`${config.get('baseUrl')}/v1/auth/validate`, { headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' }, timeout: config.getTimeoutConfig().request }); return response.data; } catch (error) { throw ErrorHandler.createAuthError('API key validation failed', { error: error.message }); } } /** * Refresh OAuth token if needed */ async refreshTokenIfNeeded() { const authConfig = config.getAuthConfig(); if (!authConfig.clientId || !authConfig.clientSecret) { return; // Cannot refresh without client credentials } if (!this.isTokenValid()) { await this.getOAuthToken(authConfig.clientId, authConfig.clientSecret); } } /** * Clear authentication state */ clearAuth() { this.currentToken = undefined; this.tokenExpiry = undefined; } /** * Get authentication status */ getAuthStatus() { const authConfig = config.getAuthConfig(); if (authConfig.apiKey) { return { method: 'api_key', isValid: true }; } if (authConfig.oauthToken) { return { method: 'oauth_token', isValid: this.isTokenValid(), expiresAt: this.tokenExpiry }; } if (authConfig.clientId && authConfig.clientSecret) { return { method: 'client_credentials', isValid: this.isTokenValid(), expiresAt: this.tokenExpiry }; } return { method: 'none', isValid: false }; } } // Export singleton instance export const authManager = new AuthManager(); //# sourceMappingURL=auth.js.map