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 ` ${title}

${title}

${body} `; } export function createWebexOAuthRoutes({ config, webexOAuth, webexWebhookManager, webexRooms, log }) { const { logger, logError } = log; const oauthStartUrl = config.webex.integration.redirectUri.replace('/oauth/callback', '/oauth/start'); function registerRoutes(app) { app.get('/webex/oauth/start', (req, res) => { if (!config.webex.integration.clientId || !config.webex.integration.clientSecret) { res.status(503).send(renderHtml( 'Webex OAuth Not Configured', '

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 => `
  • ${result.configKey}: ${result.action} (${result.webhookId})
  • ` ).join(''); res.status(200).send(renderHtml( 'Webex OAuth Complete', `

    Authentication succeeded and webhooks were registered.

    View status

    ` )); } catch (err) { logError('webexOAuth', 'OAuth callback failed', err); res.status(500).send(renderHtml( 'Webex OAuth Failed', `

    ${err.message}

    Try again

    ` )); } }); 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]) => `
  • ${roomId}: ${webhookId}
  • `) .join('') || '
  • None registered yet
  • '; res.status(200).send(renderHtml( 'Webex OAuth Status', `

    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}

    Registered webhooks

    Re-authenticate

    ` )); }); } return { registerRoutes }; }