()
| 676 | * Create WorkOS webhooks router |
| 677 | */ |
| 678 | export function createWorkOSWebhooksRouter(): Router { |
| 679 | const router = Router(); |
| 680 | |
| 681 | router.post( |
| 682 | '/workos', |
| 683 | // Parse JSON body manually since the global JSON parser is skipped for this route |
| 684 | (req: Request, res: Response, next) => { |
| 685 | let rawBody = ''; |
| 686 | req.setEncoding('utf8'); |
| 687 | req.on('data', (chunk: string) => { rawBody += chunk; }); |
| 688 | req.on('end', () => { |
| 689 | try { |
| 690 | req.body = JSON.parse(rawBody); |
| 691 | next(); |
| 692 | } catch { |
| 693 | logger.warn({ rawBodyLength: rawBody.length }, 'Invalid JSON in WorkOS webhook request'); |
| 694 | res.status(400).json({ error: 'Invalid JSON' }); |
| 695 | } |
| 696 | }); |
| 697 | }, |
| 698 | async (req: Request, res: Response) => { |
| 699 | const startTime = Date.now(); |
| 700 | |
| 701 | try { |
| 702 | const rawSigHeader = req.headers['workos-signature']; |
| 703 | const sigHeader = Array.isArray(rawSigHeader) ? rawSigHeader[0] : rawSigHeader; |
| 704 | |
| 705 | logger.info({ |
| 706 | event: req.body?.event, |
| 707 | hasSigHeader: !!sigHeader, |
| 708 | }, 'Received WorkOS webhook'); |
| 709 | |
| 710 | if (!WORKOS_WEBHOOK_SECRET) { |
| 711 | logger.error('WORKOS_WEBHOOK_SECRET not configured — rejecting webhook'); |
| 712 | notifySystemError({ source: 'workos-webhook', errorMessage: 'WORKOS_WEBHOOK_SECRET not configured — all webhooks rejected' }); |
| 713 | return res.status(401).json({ error: 'Webhook verification unavailable' }); |
| 714 | } |
| 715 | |
| 716 | if (!sigHeader) { |
| 717 | logger.warn('Missing WorkOS-Signature header'); |
| 718 | return res.status(401).json({ error: 'Missing signature' }); |
| 719 | } |
| 720 | |
| 721 | try { |
| 722 | await getWorkos().webhooks.constructEvent({ |
| 723 | payload: req.body, |
| 724 | sigHeader, |
| 725 | secret: WORKOS_WEBHOOK_SECRET, |
| 726 | }); |
| 727 | } catch (err) { |
| 728 | logger.warn({ err }, 'WorkOS webhook signature verification failed'); |
| 729 | notifySystemError({ source: 'workos-webhook-sig', errorMessage: `Signature verification failed for ${req.body?.event || 'unknown'} — check WORKOS_WEBHOOK_SECRET` }); |
| 730 | return res.status(401).json({ error: 'Invalid signature' }); |
| 731 | } |
| 732 | |
| 733 | const event = req.body as { id: string; event: string; data: Record<string, unknown>; created_at: string }; |
| 734 | |
| 735 | switch (event.event) { |
no test coverage detected