/** * Secure storage utilities for sensitive data like OAuth tokens */ const ENCRYPTION_KEY_NAME = 'mcp-encryption-key'; // Generate or retrieve encryption key async function getEncryptionKey(): Promise { const keyData = localStorage.getItem(ENCRYPTION_KEY_NAME); if (keyData) { try { const keyBuffer = new Uint8Array(JSON.parse(keyData)); return await crypto.subtle.importKey( 'raw', keyBuffer, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt'] ); } catch { // Key corrupted, generate new one } } // Generate new key const key = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ); // Store key for future use const keyBuffer = await crypto.subtle.exportKey('raw', key); localStorage.setItem(ENCRYPTION_KEY_NAME, JSON.stringify(Array.from(new Uint8Array(keyBuffer)))); return key; } // Encrypt sensitive data export async function encryptData(data: string): Promise { try { const key = await getEncryptionKey(); const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); const iv = crypto.getRandomValues(new Uint8Array(12)); const encryptedBuffer = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, key, dataBuffer ); // Combine IV and encrypted data const result = new Uint8Array(iv.length + encryptedBuffer.byteLength); result.set(iv); result.set(new Uint8Array(encryptedBuffer), iv.length); return btoa(String.fromCharCode(...result)); } catch (error) { console.warn('Encryption failed, storing data unencrypted:', error); return data; } } // Decrypt sensitive data export async function decryptData(encryptedData: string): Promise { try { const key = await getEncryptionKey(); const dataBuffer = new Uint8Array( atob(encryptedData).split('').map(char => char.charCodeAt(0)) ); const iv = dataBuffer.slice(0, 12); const encrypted = dataBuffer.slice(12); const decryptedBuffer = await crypto.subtle.decrypt( { name: 'AES-GCM', iv }, key, encrypted ); const decoder = new TextDecoder(); return decoder.decode(decryptedBuffer); } catch (error) { console.warn('Decryption failed, returning data as-is:', error); return encryptedData; } } // Secure storage wrapper for sensitive data export const secureStorage = { async setItem(key: string, value: string): Promise { const encrypted = await encryptData(value); localStorage.setItem(`secure_${key}`, encrypted); }, async getItem(key: string): Promise { const encrypted = localStorage.getItem(`secure_${key}`); if (!encrypted) return null; return await decryptData(encrypted); }, removeItem(key: string): void { localStorage.removeItem(`secure_${key}`); } };