// Platform (operator) access control. The platform operator is YOU — the // person running this SaaS for many client businesses — not a client's // business owner. Platform admins are identified by email via the // PLATFORM_ADMIN_EMAILS env var (comma-separated). The JWT carries no // email, so callers must re-read the User from the DB and pass its email // here. // // We read process.env directly (not the zod-validated getEnv) so the // platform check never couples to unrelated required env like the Daraja // M-Pesa keys. export function platformAdminEmails(): string[] { const raw = process.env.PLATFORM_ADMIN_EMAILS ?? ""; return raw .split(",") .map((e) => e.trim().toLowerCase()) .filter(Boolean); } export function isPlatformAdminEmail(email: string | null | undefined): boolean { if (!email) return false; return platformAdminEmails().includes(email.toLowerCase()); } import type { NextRequest } from "next/server"; import { getAuth } from "@/lib/server/auth"; import { User } from "@/lib/server/models/User"; /** * Whether this account is a platform operator. Two conditions, because * emails are only unique within a business: any owner can create a staff * account with any email in their own business, so an email match alone * must never grant cross-tenant access. The account must also carry the * platformAdmin flag, which the app never sets (seed/migration only). * Removing an email from PLATFORM_ADMIN_EMAILS revokes access at once. */ export function isPlatformAdmin(user: { email?: string | null; platformAdmin?: boolean | null }): boolean { return user.platformAdmin === true && isPlatformAdminEmail(user.email); } /** * Resolve and authorize the current request as a platform operator. * Returns the authenticated User's id/email on success, or null if the * caller is not a platform admin. The DB must already be connected. */ export async function requirePlatformAdmin( req: NextRequest ): Promise<{ userId: string; email: string } | null> { const auth = getAuth(req); if (!auth) return null; const user = await User.findOne({ _id: auth.sub, businessId: auth.businessId }) .select("email isActive platformAdmin") .lean(); if (!user || !user.isActive) return null; if (!isPlatformAdmin(user)) return null; return { userId: auth.sub, email: user.email }; }