You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
2.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { Permission, ServiceTokenEntry } from "@/lib/constants/auth";
import { forbidden, unauthorized } from "@/modules/auth/errors";
export type VerifiedServiceToken = {
customerId: string;
permissions: Permission[];
};
let cachedTokenMap: Map<string, ServiceTokenEntry> | null = null;
function loadTokenMap(): Map<string, ServiceTokenEntry> {
if (cachedTokenMap) {
return cachedTokenMap;
}
const raw = process.env.HOST_SERVICE_TOKENS;
if (!raw) {
cachedTokenMap = new Map();
return cachedTokenMap;
}
let parsed: Record<string, ServiceTokenEntry>;
try {
parsed = JSON.parse(raw) as Record<string, ServiceTokenEntry>;
} catch {
throw new Error("HOST_SERVICE_TOKENS 配置无效");
}
cachedTokenMap = new Map(Object.entries(parsed));
return cachedTokenMap;
}
/** 测试专用:重置缓存 */
export function resetServiceTokenCache(): void {
cachedTokenMap = null;
}
/** 校验宿主 Service Token 与 customer_id 租户绑定 */
export function verifyServiceToken(
token: string,
customerId: string,
): VerifiedServiceToken {
const entry = loadTokenMap().get(token);
if (!entry) {
throw unauthorized("Service Token 无效");
}
if (entry.customerId !== customerId) {
throw forbidden("customer_id 与令牌租户不匹配");
}
return {
customerId: entry.customerId,
permissions: entry.permissions ?? [],
};
}
/** 已注册宿主客户 ID 集合markup 写操作校验用) */
export function listRegisteredCustomerIds(): string[] {
const ids = new Set<string>();
for (const entry of loadTokenMap().values()) {
ids.add(entry.customerId);
}
const registry = process.env.CUSTOMER_REGISTRY;
if (registry) {
for (const part of registry.split(",")) {
const id = part.trim();
if (id) {
ids.add(id);
}
}
}
return [...ids].sort();
}
export function isKnownCustomer(customerId: string): boolean {
return listRegisteredCustomerIds().includes(customerId);
}
/** 仅校验 token 存在middleware 用,不含 customer 绑定) */
export function parseServiceToken(token: string): VerifiedServiceToken | null {
const entry = loadTokenMap().get(token);
if (!entry) {
return null;
}
return {
customerId: entry.customerId,
permissions: entry.permissions ?? [],
};
}