import type { Permission } from "@/lib/constants/auth"; import { forbidden } from "@/modules/auth/errors"; import type { AuthContext } from "@/modules/auth/rbac"; import { verifyServiceTokenAsync } from "@/modules/auth/service-token"; export type ServiceAuthContext = Extract; function parsePermissionsHeader(raw: string): Permission[] { return raw .split(",") .map((p) => p.trim()) .filter(Boolean) as Permission[]; } function parseVerifiedServiceAuth(request: Request): ServiceAuthContext | null { const authType = request.headers.get("x-auth-type"); if (authType !== "service") { return null; } const customerId = request.headers.get("x-customer-id"); if (!customerId) { throw forbidden("缺少客户标识"); } const verified = request.headers.get("x-service-verified"); const permissionsRaw = request.headers.get("x-permissions") ?? ""; if (verified === "true" || permissionsRaw.length > 0) { return { type: "service", customerId, permissions: parsePermissionsHeader(permissionsRaw), }; } return null; } function extractBearerFromRequest(request: Request): string | null { const forwarded = request.headers.get("x-bearer-token")?.trim(); if (forwarded) { return forwarded; } const authHeader = request.headers.get("authorization"); if (authHeader?.startsWith("Bearer ")) { return authHeader.slice(7).trim(); } return null; } /** 从 middleware 注入的请求头解析宿主鉴权上下文(含 DB Token 二次校验) */ export async function parseServiceAuth( request: Request, ): Promise { const fastPath = parseVerifiedServiceAuth(request); if (fastPath) { return fastPath; } const bearerToken = extractBearerFromRequest(request); if (!bearerToken) { throw forbidden("需要宿主 Service Token"); } const customerId = request.headers.get("x-customer-id")?.trim() || undefined; const verified = await verifyServiceTokenAsync(bearerToken, customerId); return { type: "service", customerId: verified.customerId, permissions: verified.permissions, }; } /** 校验请求体 customer_id 与 token 租户一致 */ export function assertCustomerMatch( auth: ServiceAuthContext, bodyCustomerId: string, ): void { if (auth.customerId !== bodyCustomerId) { throw forbidden("customer_id 与令牌租户不匹配"); } }