All Docs
FeaturesMaking Tax DigitalUpdated February 19, 2026

Security Advisory: Admin Panel Server-Side RBAC Not Enforced (v1.0.6)

Security Advisory: Admin Panel Server-Side RBAC Not Enforced

Version affected: ≤ 1.0.6
Severity: 🔴 Critical
Status: Disclosure & Remediation Guidance
Affected file: src/app/dashboard/admin/page.tsx


What Was Found

During a review of the v1.0.6 release, a missing access-control enforcement was identified on the Admin Panel route (/dashboard/admin).

The page guard was implemented as follows:

// src/app/dashboard/admin/page.tsx

if (!session?.user) {
  redirect('/login');
}

// In production, check RBAC role here via lib/auth.ts
// TODO: enforce owner/admin role

The conditional block only checks whether a session exists — i.e., whether the user is logged in — and contains a commented-out, never-implemented TODO for the actual role check. As a result, any authenticated user can access the Admin Panel regardless of their organisation role.


What Is Exposed

Authenticated users with no elevated privileges can:

  • Access the full Admin Panel UI at /dashboard/admin
  • Trigger and manage batch job controls
  • View system monitoring dashboards and data

This is a horizontal privilege escalation issue: a standard user (e.g. a regular landlord account) has the same effective access to the admin surface as a designated owner or admin.


Root Cause

The role check was deferred with a TODO comment referencing lib/auth.ts but was never completed before the route was shipped. The client-side application may render role-gated UI elements correctly (e.g. hiding the admin navigation link from non-admin users), but this does not protect the route itself — any user who knows or guesses the URL can navigate there directly.


Recommended Fix

The following logic should be added server-side in src/app/dashboard/admin/page.tsx, replacing the existing incomplete guard:

import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { getUserOrgMembership } from '@/lib/org';
import { redirect } from 'next/navigation';

export default async function AdminPage() {
  const session = await getServerSession(authOptions);

  if (!session?.user) {
    redirect('/login');
  }

  const membership = await getUserOrgMembership(session.user.id);

  if (!membership || !['owner', 'admin'].includes(membership.role)) {
    redirect('/dashboard');
  }

  // Safe to render admin content below
  ...
}

Key requirements

RequirementDetail
Server-side onlyThis check must run in a Next.js Server Component or getServerSideProps — never rely solely on client-side guards.
Fetch fresh membershipDo not trust role data stored in the session token alone; fetch the current membership record from the database.
Redirect, don't 404Redirecting to /dashboard avoids leaking whether the route exists while providing a safe fallback.
Accepted rolesOnly owner and admin org roles should be permitted access.

Interim Mitigation

If you cannot deploy the fix immediately, consider adding a Next.js middleware rule to restrict the /dashboard/admin path:

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/dashboard/admin')) {
    // Block access entirely until server-side RBAC is implemented
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }
}

export const config = {
  matcher: ['/dashboard/admin/:path*'],
};

This will block all users from the admin route until the proper fix is deployed. It is a blunt instrument — use it only as a temporary stopgap.


Why Client-Side Guards Are Not Sufficient

It may appear that role-gating the navigation link (e.g. only rendering an "Admin" menu item for admins) is enough. It is not. Client-side RoleGate components:

  • Can be bypassed by typing the URL directly into the browser.
  • Can be bypassed by users who disable or intercept JavaScript.
  • Rely on role data in the client session, which may be stale.

Server-side enforcement is the only reliable access-control boundary for a Next.js App Router application.


Disclosure Timeline

DateEvent
v1.0.6 releaseVulnerability identified and disclosed in release notes
ImmediateRemediation guidance published (this document)

This advisory applies to all instances of the application running versions up to and including 1.0.6 that have not applied the server-side RBAC fix described above.