msexchange-mcp
Version:
MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API
88 lines • 3.69 kB
JavaScript
import { SecretClient } from '@azure/keyvault-secrets';
import { DefaultAzureCredential } from '@azure/identity';
export class KeyVaultTokenCache {
secretClient;
cacheKeyPrefix;
cacheKey;
constructor(vaultUri, cacheKeyPrefix = 'mcp-tokens', userEmail) {
// Use DefaultAzureCredential which works with:
// - Azure CLI (local development)
// - Managed Identity (when deployed)
// - Environment variables
const credential = new DefaultAzureCredential();
this.secretClient = new SecretClient(vaultUri, credential);
this.cacheKeyPrefix = cacheKeyPrefix;
// Create a unique cache key for this user
// Replace dots and @ with hyphens for Key Vault naming rules
const safeEmail = userEmail ? userEmail.replace(/[@.]/g, '-') : 'default';
this.cacheKey = `${cacheKeyPrefix}-${safeEmail}`;
console.error(`🔐 KeyVaultTokenCache initialized for ${this.cacheKey}`);
}
async beforeCacheAccess(cacheContext) {
try {
console.error(`🔍 Retrieving token cache from Key Vault: ${this.cacheKey}`);
const secret = await this.secretClient.getSecret(this.cacheKey);
if (secret.value) {
console.error(`✅ Found cached tokens in Key Vault`);
cacheContext.tokenCache.deserialize(secret.value);
}
else {
console.error(`📭 No cached tokens found in Key Vault`);
}
}
catch (error) {
if (error.code === 'SecretNotFound') {
console.error(`📭 No cached tokens found in Key Vault (secret not found)`);
// This is fine - first time use
}
else {
console.error(`❌ Error retrieving from Key Vault:`, error.message);
// Don't throw - allow MSAL to continue with empty cache
}
}
}
async afterCacheAccess(cacheContext) {
if (cacheContext.cacheHasChanged) {
try {
const serializedCache = cacheContext.tokenCache.serialize();
console.error(`💾 Saving updated token cache to Key Vault: ${this.cacheKey}`);
await this.secretClient.setSecret(this.cacheKey, serializedCache, {
contentType: 'application/json',
tags: {
'app': 'exchange-mcp',
'type': 'msal-token-cache',
'updated': new Date().toISOString()
}
});
console.error(`✅ Token cache saved to Key Vault successfully`);
}
catch (error) {
console.error(`❌ Error saving to Key Vault:`, error.message);
// Don't throw - tokens will still work for current session
}
}
}
// Helper method to clear cache for a user
async clearCache() {
try {
console.error(`🗑️ Deleting token cache from Key Vault: ${this.cacheKey}`);
const poller = await this.secretClient.beginDeleteSecret(this.cacheKey);
await poller.pollUntilDone();
console.error(`✅ Token cache deleted from Key Vault`);
}
catch (error) {
console.error(`❌ Error deleting from Key Vault:`, error.message);
}
}
// Helper method to check if cache exists
async hasCache() {
try {
await this.secretClient.getSecret(this.cacheKey);
return true;
}
catch (error) {
return false;
}
}
}
//# sourceMappingURL=keyVaultTokenCache.js.map