Why Client-Side Role Checks Are Never Enough
In enterprise B2B software, hiding a button in the UI is not security. Every Server Action, API Route, and database query must independently verify both the user's session and their tenant boundary.
Security Principle: Never trust client-supplied tenant IDs or role flags; always resolve identity from the server-side session token.
Our 3-Layer Isolation Pattern at BAYAKA Yazılım
- 1Session & Origin Verification: HTTP-only signed session tokens combined with strict
Origin/HostCSRF validation. - 2Permission Matrix (`can(role, permission)`): Centralized RBAC matrix separating
SUPER_ADMIN,ADMIN,PROJECT_MANAGER,DEVELOPER, andCUSTOMER. - 3Row-Level Query Scoping: Customer portal queries explicitly scope every Prisma call with
where: { customerId: user.customerId }so cross-tenant data leakage is mathematically impossible.
export async function requirePortalProject(projectId: string) {
const user = await requireSessionUser();
if (!user.customerId) throw new HttpError(403, "forbidden");
return db.project.findFirstOrThrow({
where: { id: projectId, customerId: user.customerId },
});
}Immutable Audit Logging
Every administrative mutation—whether updating a quotation, changing a role, or exporting subscriber lists—is recorded in AuditLog with actor ID, entity reference, and timestamp.