const pendingStates = new Map(); const STATE_TTL_MS = 10 * 60 * 1000; function cleanupStates() { const now = Date.now(); for (const [state, createdAt] of pendingStates.entries()) { if (now - createdAt > STATE_TTL_MS) { pendingStates.delete(state); } } } function renderHtml(title, body) { return `
Set WEBEX_INTEGRATION_CLIENT_ID and WEBEX_INTEGRATION_CLIENT_SECRET in .env.
' )); return; } cleanupStates(); const state = webexOAuth.createOAuthState(); pendingStates.set(state, Date.now()); res.redirect(webexOAuth.buildAuthorizeUrl(state)); }); app.get('/webex/oauth/callback', async (req, res) => { const { code, state, error, error_description: errorDescription } = req.query; if (error) { res.status(400).send(renderHtml( 'Webex OAuth Failed', `${errorDescription || error}
` )); return; } cleanupStates(); if (!state || !pendingStates.has(state)) { res.status(400).send(renderHtml( 'Webex OAuth Failed', 'Invalid or expired OAuth state. Try again.
' )); return; } pendingStates.delete(state); if (!code) { res.status(400).send(renderHtml( 'Webex OAuth Failed', 'Missing authorization code.
' )); return; } try { await webexOAuth.exchangeCode(code); const accessToken = await webexOAuth.getAccessToken(); const webhookResults = await webexWebhookManager.ensureWebhooks({ accessToken, webexRooms, config, }); logger('webexOAuth', `OAuth complete; webhooks ensured for ${webhookResults.length} room(s)`); const webhookList = webhookResults.map(result => `Authentication succeeded and webhooks were registered.
${err.message}
` )); } }); app.get('/webex/oauth/status', (req, res) => { const status = webexOAuth.getStatus(); const wantsJson = req.accepts(['json', 'html']) === 'json' || req.query.format === 'json'; if (wantsJson) { res.json({ ...status, oauthStartUrl, inboundEnabled: config.webex.inboundEnabled, inboundDryRun: config.webex.inboundDryRun, webhookTargetUrl: config.webex.webhookTargetUrl, }); return; } const webhookItems = Object.entries(status.webhookIds) .map(([roomId, webhookId]) => `Authenticated: ${status.authenticated}
Access token expires: ${status.expiresAt || 'n/a'}
Refresh token expires: ${status.refreshExpiresAt || 'n/a'}
Inbound enabled: ${config.webex.inboundEnabled}
Dry run: ${config.webex.inboundDryRun}
Webhook target: ${config.webex.webhookTargetUrl}