(req: Request, res: Response, next: NextFunction)
| 588 | * Automatically refreshes expired access tokens using the refresh token |
| 589 | */ |
| 590 | export async function requireAuth(req: Request, res: Response, next: NextFunction) { |
| 591 | const isHtmlRequest = req.accepts('html') && !req.originalUrl.startsWith('/api/'); |
| 592 | |
| 593 | // Check for static admin API key first (for internal tooling) |
| 594 | if (hasValidAdminApiKey(req)) { |
| 595 | logger.debug({ path: req.path }, 'Authenticated via static admin API key'); |
| 596 | req.user = { |
| 597 | id: 'admin_api_key', |
| 598 | email: 'admin-api-key@internal', |
| 599 | firstName: 'Admin', |
| 600 | lastName: 'API Key', |
| 601 | emailVerified: true, |
| 602 | createdAt: new Date().toISOString(), |
| 603 | updatedAt: new Date().toISOString(), |
| 604 | }; |
| 605 | req.accessToken = 'admin-api-key'; |
| 606 | // Mark this request as using the static admin API key for requireAdmin check |
| 607 | (req as Request & { isStaticAdminApiKey?: boolean }).isStaticAdminApiKey = true; |
| 608 | return next(); |
| 609 | } |
| 610 | |
| 611 | // Check for WorkOS API key (for programmatic access) |
| 612 | const apiKey = await validateWorkOSApiKey(req); |
| 613 | if (apiKey) { |
| 614 | logger.debug({ path: req.path, apiKeyId: apiKey.id }, 'Authenticated via WorkOS API key'); |
| 615 | // Create a synthetic user for API key auth - the organization owns the key |
| 616 | req.user = { |
| 617 | id: `api_key_${apiKey.id}`, |
| 618 | email: `api-key@org-${apiKey.organizationId}`, |
| 619 | firstName: 'API', |
| 620 | lastName: apiKey.name, |
| 621 | emailVerified: true, |
| 622 | createdAt: new Date().toISOString(), |
| 623 | updatedAt: new Date().toISOString(), |
| 624 | }; |
| 625 | req.accessToken = 'workos-api-key'; |
| 626 | // Store API key info for permission checks |
| 627 | (req as Request & { apiKey?: ValidatedApiKey }).apiKey = apiKey; |
| 628 | // API keys are org-scoped, so the caller is a member by definition |
| 629 | (req.user as unknown as Record<string, unknown>).isMember = true; |
| 630 | |
| 631 | // Check platform ban for API key |
| 632 | const apiKeyBan = await checkPlatformBan( |
| 633 | `apikey:${apiKey.id}`, |
| 634 | () => bansDb.checkPlatformBanForApiKey(apiKey.id, apiKey.organizationId) |
| 635 | ); |
| 636 | if (apiKeyBan) { |
| 637 | logger.info({ apiKeyId: apiKey.id, banId: apiKeyBan.id }, 'API key request blocked by platform ban'); |
| 638 | return sendBanResponse(res, apiKeyBan); |
| 639 | } |
| 640 | |
| 641 | return next(); |
| 642 | } |
| 643 | |
| 644 | // Check for OAuth-issued user JWT (user SSO'd via AuthKit through the |
| 645 | // MCP OAuth flow and is now calling the REST API with that token). |
| 646 | const jwtAuth = await validateWorkOSBearerJWT(req); |
| 647 | if (jwtAuth) { |
nothing calls this directly
no test coverage detected