Skip to main content
Glama
jwt-manager.js•6.4 kB
#!/usr/bin/env node /** * Centralized JWT Token Manager v1.0.0 * @description Secure, single-source JWT token management for EuConquisto Composer MCP * @version 1.0.0 * @date January 2025 * @security Single secure location, no fallbacks for enhanced security */ import { readFileSync, existsSync, statSync } from 'fs'; import { join } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Project root detection const PROJECT_ROOT = join(__dirname, '..', '..'); /** * JWT Manager - Single Source of Truth for Token Management * Security-first approach with no fallback locations */ export class JWTManager { constructor() { this.token = null; this.tokenPath = join(PROJECT_ROOT, 'config', 'jwt-token.txt'); this.isLoaded = false; } /** * Get the secure JWT token path * @returns {string} Absolute path to JWT token file */ getTokenPath() { return this.tokenPath; } /** * Validate JWT token file exists and is accessible * @returns {object} Validation result with details */ validateTokenFile() { try { // Check if file exists if (!existsSync(this.tokenPath)) { return { valid: false, error: 'JWT token file not found', path: this.tokenPath, suggestion: 'Ensure /config/jwt-token.txt exists' }; } // Check file stats const stats = statSync(this.tokenPath); // Basic size validation (JWT should be substantial) if (stats.size < 100) { return { valid: false, error: 'JWT token file appears empty or invalid', size: stats.size, path: this.tokenPath }; } // File appears valid return { valid: true, size: stats.size, path: this.tokenPath, lastModified: stats.mtime }; } catch (error) { return { valid: false, error: `File access error: ${error.message}`, path: this.tokenPath }; } } /** * Load JWT token from secure location * @returns {string} JWT token string * @throws {Error} If token cannot be loaded */ getToken() { // Return cached token if already loaded if (this.isLoaded && this.token) { return this.token; } // Validate file first const validation = this.validateTokenFile(); if (!validation.valid) { throw new Error(`JWT Manager: ${validation.error}. Path: ${validation.path}`); } try { // Read and validate token const tokenContent = readFileSync(this.tokenPath, 'utf-8').trim(); if (!tokenContent) { throw new Error('JWT token file is empty'); } // Basic JWT format validation (should start with eyJ) if (!tokenContent.startsWith('eyJ')) { throw new Error('JWT token format appears invalid (should start with eyJ)'); } // Cache successful load this.token = tokenContent; this.isLoaded = true; return this.token; } catch (error) { throw new Error(`JWT Manager: Failed to load token - ${error.message}`); } } /** * Get token information without exposing the actual token * @returns {object} Token metadata */ getTokenInfo() { const validation = this.validateTokenFile(); if (!validation.valid) { return { available: false, error: validation.error, path: this.tokenPath }; } return { available: true, path: this.tokenPath, size: validation.size, lastModified: validation.lastModified, loaded: this.isLoaded }; } /** * Clear cached token (force reload on next access) */ clearCache() { this.token = null; this.isLoaded = false; } /** * Check if JWT token is expired (basic check) * @returns {object} Expiration status */ checkExpiration() { try { const token = this.getToken(); // Extract payload (basic JWT structure: header.payload.signature) const parts = token.split('.'); if (parts.length !== 3) { return { valid: false, error: 'Invalid JWT structure' }; } // Decode payload (base64) const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString()); if (!payload.exp) { return { valid: true, error: 'No expiration claim found' }; } const now = Math.floor(Date.now() / 1000); const expired = now > payload.exp; const timeRemaining = payload.exp - now; return { valid: !expired, expired: expired, expirationDate: new Date(payload.exp * 1000), timeRemaining: timeRemaining, timeRemainingHours: Math.floor(timeRemaining / 3600) }; } catch (error) { return { valid: false, error: `Failed to check expiration: ${error.message}` }; } } } /** * Singleton instance for application-wide use */ export const jwtManager = new JWTManager(); /** * Convenience functions for backward compatibility */ export function getJWTToken() { return jwtManager.getToken(); } export function getJWTPath() { return jwtManager.getTokenPath(); } export function getJWTInfo() { return jwtManager.getTokenInfo(); } /** * CLI usage for testing */ if (import.meta.url === `file://${process.argv[1]}`) { console.log('šŸ” JWT Manager v1.0.0 - Token Information'); console.log('=' .repeat(50)); try { const info = jwtManager.getTokenInfo(); console.log('šŸ“ Token Path:', info.path); console.log('āœ… Available:', info.available); if (info.available) { console.log('šŸ“ Size:', info.size, 'bytes'); console.log('šŸ“… Last Modified:', info.lastModified); // Test expiration check const expiration = jwtManager.checkExpiration(); if (expiration.valid) { console.log('ā° Expires:', expiration.expirationDate); console.log('ā³ Time Remaining:', expiration.timeRemainingHours, 'hours'); } else { console.log('āš ļø Expiration Check:', expiration.error); } } else { console.log('āŒ Error:', info.error); } } catch (error) { console.error('āŒ JWT Manager Error:', error.message); process.exit(1); } }

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/rkm097git/euconquisto-composer-mcp-poc'

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