UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

209 lines • 7.45 kB
/** * SharePoint OAuth2 Authentication Handler * Part of ADPA (Automated Document Processing Assistant) v2.1.3 * * Handles OAuth 2.0 authentication flow for Microsoft Graph API and SharePoint access. * Implements MSAL (Microsoft Authentication Library) for secure token management. * * Features: * - Interactive authentication flow * - Token refresh and caching * - Multi-tenant support * - Device code flow for headless environments * - Secure token storage */ import { PublicClientApplication } from '@azure/msal-node'; import fs from 'fs'; import path from 'path'; export class SharePointOAuth2 { config; msalApp; tokenCache = new Map(); cacheFilePath; constructor(config) { this.config = config; this.cacheFilePath = config.cacheLocation || path.join(process.cwd(), '.sharepoint-cache.json'); // Configure MSAL const msalConfig = { auth: { clientId: config.clientId, authority: config.authority || `https://login.microsoftonline.com/${config.tenantId}`, knownAuthorities: [`${config.tenantId}.b2clogin.com`] }, system: { loggerOptions: { loggerCallback: (level, message, containsPii) => { if (!containsPii) { console.log(`[MSAL ${level}] ${message}`); } }, piiLoggingEnabled: false, logLevel: 3 // Info level } } }; this.msalApp = new PublicClientApplication(msalConfig); this.loadTokenCache(); } /** * Start interactive OAuth2 authentication flow * @returns Promise with authentication result */ async startInteractiveFlow() { try { console.log('šŸ” Starting device code authentication (recommended for CLI)...'); return await this.startDeviceCodeFlow(); } catch (error) { console.error('āŒ Authentication failed:', error.message); throw error; } } /** * Start device code authentication flow (for headless environments) * @returns Promise with authentication result */ async startDeviceCodeFlow() { try { const request = { scopes: this.config.scopes, deviceCodeCallback: (response) => { console.log('\nšŸ”‘ Device Code Authentication'); console.log(` Go to: ${response.verificationUri}`); console.log(` Enter code: ${response.userCode}`); console.log(' Waiting for authentication...\n'); } }; console.log('šŸ” Starting device code authentication...'); const authResult = await this.msalApp.acquireTokenByDeviceCode(request); if (authResult) { await this.saveTokenCache(authResult); console.log('āœ… Device code authentication successful'); return authResult; } else { throw new Error('Device code authentication failed'); } } catch (error) { console.error('āŒ Device code authentication failed:', error.message); throw error; } } /** * Get valid access token (handles refresh automatically) * @returns Valid access token */ async getValidAccessToken() { try { // Try to get cached account const accounts = await this.msalApp.getTokenCache().getAllAccounts(); if (accounts.length === 0) { throw new Error('No cached accounts found. Please authenticate first.'); } const account = accounts[0]; // Try silent token acquisition const silentRequest = { scopes: this.config.scopes, account: account }; try { const result = await this.msalApp.acquireTokenSilent(silentRequest); if (result?.accessToken) { return result.accessToken; } } catch (silentError) { console.log('āš ļø Silent token acquisition failed, trying refresh...'); } // If silent acquisition fails, try interactive flow console.log('šŸ”„ Refreshing authentication...'); const authResult = await this.startInteractiveFlow(); return authResult.accessToken; } catch (error) { console.error('āŒ Failed to get valid access token:', error.message); throw error; } } /** * Check if user is currently authenticated * @returns Boolean indicating authentication status */ async isAuthenticated() { try { const accounts = await this.msalApp.getTokenCache().getAllAccounts(); return accounts.length > 0; } catch (error) { return false; } } /** * Get current user account information * @returns Account information or null */ async getCurrentAccount() { try { const accounts = await this.msalApp.getTokenCache().getAllAccounts(); return accounts.length > 0 ? accounts[0] : null; } catch (error) { return null; } } /** * Sign out current user */ async signOut() { try { const accounts = await this.msalApp.getTokenCache().getAllAccounts(); for (const account of accounts) { await this.msalApp.getTokenCache().removeAccount(account); } // Clear local cache this.tokenCache.clear(); if (fs.existsSync(this.cacheFilePath)) { fs.unlinkSync(this.cacheFilePath); } console.log('āœ… Successfully signed out'); } catch (error) { console.error('āŒ Sign out error:', error.message); throw error; } } /** * Load token cache from file */ loadTokenCache() { try { if (fs.existsSync(this.cacheFilePath)) { const cacheData = fs.readFileSync(this.cacheFilePath, 'utf-8'); const cache = JSON.parse(cacheData); this.tokenCache = new Map(Object.entries(cache)); console.log('āœ… Token cache loaded'); } } catch (error) { console.log('āš ļø Could not load token cache, starting fresh'); this.tokenCache = new Map(); } } /** * Save token cache to file * @param authResult Authentication result to cache */ async saveTokenCache(authResult) { try { const cacheData = { accessToken: authResult.accessToken, expiresOn: authResult.expiresOn?.toISOString(), account: authResult.account }; fs.writeFileSync(this.cacheFilePath, JSON.stringify(cacheData, null, 2)); console.log('āœ… Token cache saved'); } catch (error) { console.warn('āš ļø Could not save token cache:', error.message); } } } //# sourceMappingURL=SharePointOAuth2.js.map