Skip to content

Interactive demo

RBAC playground

Switch roles and watch the menu and the guard answer together. Then switch the wiring to broken, and watch the menu keep offering a page the guard will refuse.
Signed in as
Wiring

Menu and guard both read the permission map.

the app

Billing

guard: billing.manage

the permission map

granted = [dashboard.read, reports.read, audit.read, billing.manage, settings.manage]

  • dashboarddashboard.read
  • reportsreports.read
  • auditaudit.read
  • billingbilling.manage
  • settingssettings.manage

Menu items the guard refuses

0

— zero by construction: the menu is the map, filtered.

What just happened

Navigation visibility and route authorisation are the same question asked twice: may this person open this module? In derived mode it is asked once. One permission map answers it, the menu is that map filtered, and the guard is that map checked. The count of menu items the guard would refuse is zero, and it is zero by construction rather than by vigilance.

In broken mode the menu comes from a hand-written list instead. Nothing about that list is stupid — it is what a menu looks like when somebody writes it at build time, and it is correct on the day it ships. It goes wrong quietly, later, when a permission changes and only the guard is updated. Switch to viewer and Billing is still in the menu; open it and you are on a 403 with no way forward.

Why the 403 is the point

Nothing crashed. No error was logged. Two lists simply fell out of step, and the person who clicked now believes the application is broken — or worse, that they were supposed to have access and something took it away. That is the cost of the second list, and it is paid in trust rather than in exceptions.

The trade-off

Deriving the menu from permissions means the menu is only as expressive as the map. Ordering, grouping, a module that should be visible but disabled rather than hidden — each of those has to become a property of the map instead of a decision made in the menu. That is more upfront design than a hand-written list, and it is the whole cost.

Worth naming the other half: hiding a menu item is not authorisation. The guard is what enforces the rule, and the server is what enforces the guard. A derived menu removes a confusing dead end from the interface — it is not a security boundary, and nothing here treats it as one.

What is simulated

The roles, permissions and modules are generic and invented for this page. There is no server: the map is a plain object, the guard is a set lookup, and both run in the browser.