msexchange-mcp
Version:
MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API
112 lines • 4.7 kB
JavaScript
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