UNPKG

dbx-mcp-server

Version:

A Model Context Protocol server for Dropbox integration with AI-powered PDF analysis, multi-directory indexing, and parallel processing

87 lines 3.72 kB
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import * as dotenv from 'dotenv'; dotenv.config(); function getValidatedKey() { const key = process.env.TOKEN_ENCRYPTION_KEY || ''; if (!key) { throw new McpError(ErrorCode.InvalidParams, 'TOKEN_ENCRYPTION_KEY must be set in environment variables'); } // Convert base64 key to buffer if it's base64 encoded const validatedKey = key.includes('+') || key.includes('/') || key.includes('=') ? Buffer.from(key, 'base64') : Buffer.from(key); // Ensure key is 32 bytes if (validatedKey.length < 32) { throw new McpError(ErrorCode.InvalidParams, 'TOKEN_ENCRYPTION_KEY must be at least 32 bytes when decoded'); } return validatedKey; } const ALGORITHM = 'aes-256-gcm'; export function encryptData(data) { try { // Generate a random initialization vector const iv = randomBytes(16); // Create cipher with key and iv const cipher = createCipheriv(ALGORITHM, getValidatedKey().slice(0, 32), iv); // Convert data to JSON string const jsonStr = JSON.stringify(data); // Encrypt the data let encryptedData = cipher.update(jsonStr, 'utf8', 'hex'); encryptedData += cipher.final('hex'); // Get the auth tag const authTag = cipher.getAuthTag(); // Combine the encrypted data and auth tag const finalEncryptedData = encryptedData + authTag.toString('hex'); return { iv: iv.toString('hex'), encryptedData: finalEncryptedData }; } catch (error) { console.error('Encryption error:', error); throw new McpError(ErrorCode.InternalError, 'Failed to encrypt token data'); } } export function decryptData(encryptedData) { try { // Extract the auth tag from the end of the encrypted data (last 16 bytes) const authTagLength = 32; // 16 bytes in hex = 32 characters const encryptedHex = encryptedData.encryptedData; const authTag = Buffer.from(encryptedHex.slice(-authTagLength), 'hex'); const encryptedContent = encryptedHex.slice(0, -authTagLength); // Create decipher const decipher = createDecipheriv(ALGORITHM, getValidatedKey().slice(0, 32), Buffer.from(encryptedData.iv, 'hex')); // Set auth tag decipher.setAuthTag(authTag); // Decrypt the data let decrypted = decipher.update(encryptedContent, 'hex', 'utf8'); decrypted += decipher.final('utf8'); if (!decrypted) { throw new Error('Decryption produced empty result'); } return JSON.parse(decrypted); } catch (error) { console.error('Decryption error:', error); throw new McpError(ErrorCode.InternalError, 'Failed to decrypt token data. The data may be corrupted or the encryption key may be invalid.'); } } export function validateCorsOrigin(origin) { const allowedOrigins = process.env.CORS_ALLOWED_ORIGINS?.split(',') || []; return allowedOrigins.includes(origin); } export class TokenRefreshError extends Error { constructor(message, code, retryable = true) { super(message); this.code = code; this.retryable = retryable; this.name = 'TokenRefreshError'; } } export const TOKEN_REFRESH_CONFIG = { maxRetries: parseInt(process.env.MAX_TOKEN_REFRESH_RETRIES || '3', 10), retryDelay: parseInt(process.env.TOKEN_REFRESH_RETRY_DELAY_MS || '1000', 10), thresholdMinutes: parseInt(process.env.TOKEN_REFRESH_THRESHOLD_MINUTES || '5', 10) }; //# sourceMappingURL=security-utils.js.map