UNPKG

twenty-mcp-server

Version:

Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.

111 lines 3.47 kB
import { ClerkClient } from './clerk-client.js'; import { getEncryptionService } from './encryption.js'; /** * Service for securely storing and retrieving user API keys * Uses Clerk's privateMetadata with encryption */ export class KeyStorageService { clerkClient; encryptionService = getEncryptionService(); constructor() { this.clerkClient = new ClerkClient(); } /** * Store an encrypted API key for a user */ async storeApiKey(userId, apiKey, baseUrl) { if (!userId || !apiKey) { throw new Error('User ID and API key are required'); } // Encrypt the API key const encryptedKey = this.encryptionService.encrypt(apiKey); // Prepare metadata const metadata = { twentyApiKey: encryptedKey, twentyBaseUrl: baseUrl, twentyApiKeyUpdatedAt: new Date().toISOString(), }; // Store in Clerk await this.clerkClient.updateUserMetadata(userId, metadata); } /** * Retrieve and decrypt a user's API key */ async getApiKey(userId) { if (!userId) { throw new Error('User ID is required'); } // Get user metadata from Clerk const metadata = await this.clerkClient.getUserMetadata(userId); if (!metadata?.twentyApiKey) { return null; } try { // Decrypt the API key const decryptedKey = this.encryptionService.decrypt(metadata.twentyApiKey); return { twentyApiKey: decryptedKey, twentyBaseUrl: metadata.twentyBaseUrl || undefined, twentyApiKeyUpdatedAt: metadata.twentyApiKeyUpdatedAt || new Date().toISOString(), }; } catch (error) { console.error('Failed to decrypt API key:', error); return null; } } /** * Delete a user's API key */ async deleteApiKey(userId) { if (!userId) { throw new Error('User ID is required'); } // Clear the metadata await this.clerkClient.updateUserMetadata(userId, { twentyApiKey: undefined, twentyBaseUrl: undefined, twentyApiKeyUpdatedAt: undefined, }); } /** * Validate if a stored API key matches a given key */ async validateApiKey(userId, apiKey) { const stored = await this.getApiKey(userId); if (!stored) { return false; } return stored.twentyApiKey === apiKey; } /** * Rotate a user's API key */ async rotateApiKey(userId, newApiKey, baseUrl) { // Store the new key (this will overwrite the old one) await this.storeApiKey(userId, newApiKey, baseUrl); } /** * Get API key metadata without decrypting */ async getApiKeyMetadata(userId) { const metadata = await this.clerkClient.getUserMetadata(userId); return { hasKey: !!metadata?.twentyApiKey, updatedAt: metadata?.twentyApiKeyUpdatedAt, baseUrl: metadata?.twentyBaseUrl, }; } } // Singleton instance let keyStorageService = null; /** * Get or create the key storage service instance */ export function getKeyStorageService() { if (!keyStorageService) { keyStorageService = new KeyStorageService(); } return keyStorageService; } //# sourceMappingURL=key-storage.js.map