UNPKG

msexchange-mcp

Version:

MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API

112 lines 4.7 kB
import { PublicClientApplication } from '@azure/msal-node'; import { Client } from '@microsoft/microsoft-graph-client'; import * as fs from 'fs'; import * as path from 'path'; // Use a singleton instance for the MSAL client let msalInstance = null; export class SimpleAuthService { msalClient; constructor(config) { if (!msalInstance) { // Use stderr for debug output to avoid breaking JSON-RPC protocol console.error('🔧 Creating new MSAL PublicClientApplication instance'); msalInstance = new PublicClientApplication({ auth: { clientId: config.clientId, authority: `https://login.microsoftonline.com/${config.tenantId}`, }, }); } this.msalClient = msalInstance; } async getTokenForUser(userEmail) { console.error(`🔑 Getting token for ${userEmail}`); // First try to get cached account const accounts = await this.msalClient.getTokenCache().getAllAccounts(); console.error(`📊 Found ${accounts.length} cached accounts`); if (accounts.length > 0) { try { console.error('🔄 Attempting silent token acquisition...'); const result = await this.msalClient.acquireTokenSilent({ scopes: ['https://graph.microsoft.com/.default', 'offline_access'], account: accounts[0], // Use first account for now }); if (result) { console.error('✅ Got token from cache!'); return result.accessToken; } } catch (error) { console.error('❌ Silent acquisition failed, will use device code'); } } // Use device code flow console.error('🔐 Starting device code flow...'); const deviceCodeRequest = { scopes: ['https://graph.microsoft.com/.default', 'offline_access'], deviceCodeCallback: (response) => { console.error('\n' + '='.repeat(50)); console.error('🔐 DEVICE CODE AUTHENTICATION'); console.error('='.repeat(50)); console.error(`📱 Visit: ${response.verificationUri}`); console.error(`🔑 Code: ${response.userCode}`); console.error('='.repeat(50) + '\n'); // Write to file for Claude try { fs.writeFileSync(path.join(process.cwd(), '.auth-pending.json'), JSON.stringify({ verificationUri: response.verificationUri, userCode: response.userCode, message: response.message, }, null, 2)); } catch (error) { // Ignore } }, }; try { const result = await this.msalClient.acquireTokenByDeviceCode(deviceCodeRequest); if (!result) { throw new Error('No result from device code flow'); } console.error('✅ Device code authentication successful!'); return result.accessToken; } catch (error) { console.error('❌ Device code flow failed:', error); throw error; } } // Force token refresh by clearing cache and getting new token async forceTokenRefresh(userEmail) { console.error(`🔄 Forcing token refresh for ${userEmail}`); // Clear the token cache await this.msalClient.getTokenCache().removeAccount((await this.msalClient.getAllAccounts())[0]); // Get fresh token return await this.getTokenForUser(userEmail); } // Clear token cache for a specific user async clearTokenCache(userEmail) { console.error(`🗑️ Clearing token cache for ${userEmail}`); const accounts = await this.msalClient.getAllAccounts(); for (const account of accounts) { await this.msalClient.getTokenCache().removeAccount(account); } } // Clear all cached tokens async clearAllTokens() { console.error(`🗑️ Clearing all cached tokens`); const accounts = await this.msalClient.getAllAccounts(); for (const account of accounts) { await this.msalClient.getTokenCache().removeAccount(account); } } getGraphClient(accessToken) { return Client.init({ authProvider: (done) => { done(null, accessToken); }, }); } } //# sourceMappingURL=authService-simple.js.map