# How requireRole Composes with requirePlatformAdmin for RBAC in DeskcommCRM

> Understand how requireRole composes with requirePlatformAdmin for DeskcommCRM RBAC. Explore two-layer role enforcement and platform admin bypass for robust access control.

- Repository: [Rafael Melgaço/DeskcommCRM](https://github.com/melgarafael/DeskcommCRM)
- Tags: architecture
- Published: 2026-09-13

---

**In DeskcommCRM, `requireRole` and `requirePlatformAdmin` form a two-layer RBAC matrix where the tenant-level guard enforces organization-specific role ranks via database lookups while optionally allowing platform admins to bypass rank checks using the `allowPlatformAdmin` flag, and the platform-level guard validates global super-admin status and MFA compliance before any cross-tenant operations proceed.**

The DeskcommCRM back-end protects every `/api/v1/*` endpoint with a layered authorization strategy that separates tenant-specific permissions from platform-wide administrative privileges. Understanding how `requireRole` in [`lib/auth/require-role.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/require-role.ts) composes with `requirePlatformAdmin` is essential for implementing secure, auditable role-based access control across multi-tenant operations.

## The Two-Layer RBAC Architecture

The RBAC matrix is implemented through two distinct authorization layers:

- **Tenant (Organization) Level**: Enforces the user’s effective role inside the active organization using `requireRole(minRole, { allowPlatformAdmin? })` in [`lib/auth/require-role.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/require-role.ts). This guard queries the database to determine rank and blocks insufficient privileges.

- **Platform (Super-Admin) Level**: Validates that the caller is a cross-tenant super-user with the `requirePlatformAdmin()` guard in [`lib/auth/requirePlatformAdmin.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/requirePlatformAdmin.ts). This layer ensures MFA AAL2 compliance before allowing global administrative actions.

## Tenant-Scoped Authorization with requireRole

The `requireRole` function implements a strict, database-driven permission check. According to the source in [`lib/auth/require-role.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/require-role.ts) (lines 55-90), the execution flow follows these steps:

1. **Load Authentication**: Retrieve the authenticated user via `loadAuthUser`.
2. **Resolve Organization**: Determine the active organization through `resolveActiveOrg`, or look up the specific `organizationId` from the user’s memberships if provided.
3. **Platform Admin Bypass**: If the `allowPlatformAdmin` option is `true` **and** the user is a platform admin **and** the request is not a support-session, the guard short-circuits and returns success (`ok: true`).
4. **Database Role Lookup**: Query the effective role via the PostgreSQL RPC `fn_user_role_in_org`.
5. **Rank Comparison**: Compare the numeric rank from `ROLE_RANK` against the requested `min` parameter. If the rank is insufficient, the guard emits an audit entry and returns a `403` response with code `forbidden_role`.

Between lines 15-35, the guard enforces MFA requirements. Even if the user possesses adequate rank, failing the MFA check results in a `403` with `mfa_required`. Notably, **the role is always fetched from the database**, never from the JWT or cookie snapshot, preventing stale permission exploits.

## Global Super-Admin Validation with requirePlatformAdmin

The `requirePlatformAdmin` guard in [`lib/auth/requirePlatformAdmin.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/requirePlatformAdmin.ts) (lines 34-70) establishes the global super-admin barrier:

1. **User Verification**: Retrieve the session via `supabase.auth.getUser`.
2. **Admin Table Check**: Verify the existence of an active row in the RLS-protected `platform_admins` table.
3. **MFA Enforcement**: If the admin’s `mfa_required` metadata flag is true, validate MFA AAL2 using `supabase.auth.mfa.getAuthenticatorAssuranceLevel`.
4. **Failure Handling**: On any validation failure, the guard **redirects** the user to the appropriate login or forbidden page rather than returning a JSON error.

On success, the function returns a `PlatformAdminContext` containing the raw Supabase `User` object and the `platformAdmin` metadata, enabling subsequent operations to identify the acting super-admin.

## Composition Patterns for the RBAC Matrix

The two guards are designed to interoperate rather than conflict, creating three distinct authorization patterns:

### 1. Admin-Only Routes

Routes under `/admin/*` invoke `requirePlatformAdmin()` first. For example, in [`app/api/v1/admin/users/route.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/app/api/v1/admin/users/route.ts), the guard validates global admin status and MFA before processing user management operations.

### 2. Tenant Operations Within Admin Context

When a platform admin performs actions on a specific tenant (such as impersonation), the code first calls `requirePlatformAdmin()`, then invokes `requireRole` with `allowPlatformAdmin: true`:

```typescript
// app/api/v1/admin/tenants/[id]/impersonate/route.ts
import { requirePlatformAdmin } from "@/lib/auth/requirePlatformAdmin";
import { requireRole } from "@/lib/auth/require-role";

export async function POST(req: Request, { params }: { params: { id: string } }) {
  const adminCtx = await requirePlatformAdmin(); // Validates global admin + MFA

  // Bypass tenant rank check for platform admins
  const authz = await requireRole("admin", {
    organizationId: params.id,
    allowPlatformAdmin: true,
  });
  if (!authz.ok) return authz.response;

  // Perform impersonation logic using adminCtx.user
}

```

### 3. Pure-Tenant Routes

Standard organization endpoints call `requireRole` without the bypass flag. This forces the database rank check via `fn_user_role_in_org` and ensures only users with appropriate tenant roles proceed:

```typescript
// app/api/v1/contatos/route.ts
import { requireRole } from "@/lib/auth/require-role";

export async function POST(req: Request) {
  const authz = await requireRole("manager");
  if (!authz.ok) return authz.response;

  // authz.user and authz.org are guaranteed valid
}

```

### Support Session Exclusions

The `allowPlatformAdmin` bypass explicitly excludes support sessions. When operating in a support session context, platform admins must still satisfy the tenant’s role hierarchy, preventing privilege escalation during customer support scenarios.

## Practical Implementation Examples

### Protecting Admin Layouts

Server components use `requirePlatformAdmin` to gate entire UI sections:

```typescript
// app/admin/layout.tsx
import { requirePlatformAdmin } from "@/lib/auth/requirePlatformAdmin";

export default async function AdminLayout({ children }: { children: React.ReactNode }) {
  await requirePlatformAdmin(); // Redirects on failure
  return <>{children}</>;
}

```

### Hybrid Authorization with Audit Trails

When `allowPlatformAdmin` enables bypass, `requireRole` still performs tenant resolution and emits audit entries. This ensures platform admin actions are logged against the target organization while skipping the rank comparison.

## Summary

- **`requireRole`** enforces tenant-specific permissions by querying the database via `fn_user_role_in_org` and comparing against `ROLE_RANK`, with optional MFA checks.
- **`requirePlatformAdmin`** validates global super-admin status against the `platform_admins` table and enforces MFA AAL2, redirecting on failure.
- **Composition** occurs through the `allowPlatformAdmin` flag, allowing validated platform admins to bypass tenant rank checks while maintaining audit trails.
- **Support sessions** disable the platform admin bypass, ensuring least-privilege during customer assistance.
- **Helper utilities** including `loadAuthUser` and `resolveActiveOrg` in [`lib/auth/server.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/server.ts) provide the foundational session and organization resolution for both guards.

## Frequently Asked Questions

### How does the `allowPlatformAdmin` flag change requireRole behavior?

When `allowPlatformAdmin` is set to `true` in the options object, `requireRole` checks if the user is a platform admin before querying the database for the tenant role. If the user is a global admin and not in a support session, the function returns immediately with `ok: true`, bypassing the `ROLE_RANK` comparison while still resolving the target organization for audit purposes.

### When should I use requirePlatformAdmin versus requireRole?

Use `requirePlatformAdmin` for routes that manage global system settings, cross-tenant user administration, or platform-wide configurations. Use `requireRole` for organization-scoped operations such as managing contacts, deals, or internal tenant users. When building admin tools that manipulate specific tenant data, compose both guards by calling `requirePlatformAdmin` first, then `requireRole` with `allowPlatformAdmin: true`.

### How is MFA enforced across both authorization layers?

MFA enforcement happens independently in each layer. `requirePlatformAdmin` checks for MFA AAL2 compliance specifically for platform administrators using `supabase.auth.mfa.getAuthenticatorAssuranceLevel`. Separately, `requireRole` (lines 15-35) validates MFA status for tenant users after the rank check but before allowing the request to proceed, returning `mfa_required` if the assurance level is insufficient.

### What prevents platform admins from bypassing roles during support sessions?

The bypass logic in [`lib/auth/require-role.ts`](https://github.com/melgarafael/DeskcommCRM/blob/main/lib/auth/require-role.ts) explicitly checks that the request is not a support-session before allowing platform admins to skip the rank check. This ensures that when admins use support-session functionality to assist customers, they operate under the same `ROLE_RANK` constraints as the customer’s organization members, maintaining security boundaries during troubleshooting.