| import type { Request, Response, NextFunction } from "express"; |
| import { getAuth, clerkClient } from "@clerk/express"; |
|
|
| export interface AuthedRequest extends Request { |
| userId: string; |
| userEmail: string | null; |
| } |
|
|
| const emailCache = new Map<string, { email: string | null; ts: number }>(); |
| const CACHE_TTL_MS = 15 * 60 * 1000; |
|
|
| export async function requireAuth( |
| req: Request, |
| res: Response, |
| next: NextFunction, |
| ): Promise<void> { |
| const auth = getAuth(req); |
| const claimUserId = |
| typeof auth?.sessionClaims?.["userId"] === "string" |
| ? (auth.sessionClaims["userId"] as string) |
| : null; |
| const userId: string | null = claimUserId ?? auth?.userId ?? null; |
| if (!userId) { |
| res.status(401).json({ error: "Unauthorized" }); |
| return; |
| } |
|
|
| let email: string | null = null; |
| const cached = emailCache.get(userId); |
| if (cached && Date.now() - cached.ts < CACHE_TTL_MS) { |
| email = cached.email; |
| } else { |
| try { |
| const user = await clerkClient.users.getUser(userId); |
| email = user.primaryEmailAddress?.emailAddress ?? null; |
| } catch (err) { |
| req.log.warn({ err }, "Failed to fetch user from Clerk"); |
| } |
| emailCache.set(userId, { email, ts: Date.now() }); |
| } |
|
|
| (req as AuthedRequest).userId = userId; |
| (req as AuthedRequest).userEmail = email; |
| next(); |
| } |
|
|