// Session-derived types & helpers — keep all session-shape knowledge in // one place so route components don't all repeat the same casts. // // The breakpilot-dev realm projects these claims into every JWT via // protocol mappers (see platform/orca-platform/dev/keycloak/realm-export.json). // Auth.js v5 callbacks copy them onto the session in src/auth.ts. import type { Session } from "next-auth"; export type OrgRole = "IT_ADMIN" | "CXO" | "FINANCE" | "LEGAL" | "USER"; export type TenantStatus = "demo" | "trial" | "active" | "frozen" | "archived"; export type Plan = "starter" | "professional" | "enterprise"; export type SessionExtras = { tenant_id?: string; tenant_slug?: string; org_roles?: OrgRole[]; products?: string[]; plan?: Plan; tenant_status?: TenantStatus; }; export type SessionWithExtras = Session & SessionExtras; export function hasOrgRole(s: SessionWithExtras | null, role: OrgRole): boolean { return !!s?.org_roles?.includes(role); } export function hasAnyOrgRole(s: SessionWithExtras | null, roles: OrgRole[]): boolean { if (!s?.org_roles) return false; return roles.some((r) => s.org_roles?.includes(r)); } export function hasProduct(s: SessionWithExtras | null, product: string): boolean { return !!s?.products?.includes(product); } // Permission matrix per PLATFORM_ARCHITECTURE.md §5a "Operating principles": // hide what the user can't access. Each portal surface declares which // org_roles can see it; the nav uses this to filter links. export type Surface = | "dashboard" | "products" | "projects" | "settings" | "users" | "api-keys" | "integrations" | "billing" | "audit" | "support" | "catalog"; export const surfaceRoles: Record = { dashboard: ["IT_ADMIN", "CXO", "FINANCE", "LEGAL", "USER"], products: ["IT_ADMIN", "CXO", "FINANCE", "LEGAL", "USER"], projects: ["IT_ADMIN", "CXO"], settings: ["IT_ADMIN"], users: ["IT_ADMIN"], "api-keys": ["IT_ADMIN"], integrations: ["IT_ADMIN"], billing: ["IT_ADMIN", "CXO", "FINANCE"], audit: ["IT_ADMIN", "CXO", "LEGAL"], support: ["IT_ADMIN", "CXO", "FINANCE", "LEGAL", "USER"], catalog: ["IT_ADMIN", "CXO"], }; export function canSee(s: SessionWithExtras | null, surface: Surface): boolean { return hasAnyOrgRole(s, surfaceRoles[surface]); }