@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
JavaScript
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