Examples

Examples of RBAC functionality in Nocto

Availability:

RBAC framework is available from the following versions:

  • CLI (npx @rsc-labs/create-nocto-app) - >= 0.0.19
  • @rsc-labs/nocto - >= 0.1.6
  • @rsc-labs/nocto-plugin-system - >= 0.1.5

Basic example

Availability:

Even with basic example we encourage to get familiar firstly with RBAC Concept

Background

You have 2 users in database - one of them has the role Admin and second one is the Product manager.

Admin can do everything, while Product manager can work only with products and inventory.

You are storing the users with assigned roles. You expose usual Medusa API - /admin/users/{id}, which can be used to retrieve single user. You are storing the role in metadata field, so it is available when fetching the user.

Implementation

You need to get list of permissions for the logged-in user. You do not store any permissions related to Nocto, so you will need to have mapping between your backend model and Nocto.

This is the example of fetchPermission function:

fetchPermissions: async (userId: string) => {
  const response = await fetch(`${import.meta.env.VITE_MEDUSA_ADMIN_BACKEND_URL}/admin/users/${userId}`, {
    method: "GET",
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
    },
  })
  const json = await response.json()
  /*
    Example of json:
    {
        "id": "user_01J8M9MBE14VGDPPJ5YZ3VG94S",
        "first_name": "Admin",
        "last_name": "Test1",
        "email": "admin@medusa-test.com",
        "avatar_url": null,
        "metadata": {
          "role": "product-manager"
        },
        "created_at": "2024-09-25T09:54:05.889Z",
        "updated_at": "2025-07-18T10:27:34.615Z",
        "deleted_at": null
    }
  */

  if (json.user.metadata.role === 'product-manager') {
    return new Map<string, string[]>([
      ["@customers", ["forbid"]],
      ["@orders", ["forbid"]],
      ["@price-lists", ["forbid"]],
      ["@promotions", ["forbid"]],
    ]);
  }
  return new Map<string, string[]>();
},

Environment variable:

In the above code we are using env variable called VITE_MEDUSA_ADMIN_BACKEND_URL which is not set by default in Nocto.

Let's describe above code:

  1. You are getting user from the backend using Medusa User ID
  2. Based on the information stored in database you are doing mapping of a role to Nocto permissions. For the role product-manager you are describing what Nocto plugins (it can be a route, UI element etc) are forbidden. For the rest - you are returning empty map.

Worth to note:

For simplicity, we decided to use negative permission (forbid), but you can also return positive permission (like allow or view) and evaluate it later.

Evaluate access

Above information about permissions can now be used to evaluate access to particular piece of UI. It is done via a function evaluateAccess. This function is executed for every plugin in Nocto to decide if it shall be rendered for the logged-in user.

The function gets list of permissions and returns decision (boolean) if the plugin shall be rendered.

For instance, when Nocto renders @products plugin, it executed evaluateAccess function with permissions related to @products plugin.

Keeping above example of permissions from fetchPermissions, evaluateAccess can be implemented in the following way:

evaluateAccess: (permissions?: string[]) => {
  if (permissions?.includes("forbid") ) {
    return false;
  }
  return true;
},

It means that if permissions contain forbid permission, then we do not want to render such plugin for the user.

Freedom:

You may notice that you have full freedom how to handle RBAC. Function fetchPermissions can return whatever values for Nocto plugins and you will decide in evaluateAccess how use these values.

The full code of your Nocto application (main.tsx) will look like this:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { loadMyNoctoPlugins } from './utils';
import { noctoConfig } from './../nocto-config'
import App from "@rsc-labs/nocto"
import "./index.css"
import "./themes/theme-oceanic.css"

async function bootstrap() {
  await loadMyNoctoPlugins(noctoConfig);

  ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
      <App noctoConfig={noctoConfig} rbac={{
        fetchPermissions: async (userId: string) => {
            const response = await fetch(`${import.meta.env.VITE_MEDUSA_ADMIN_BACKEND_URL}/admin/users/${userId}`, {
              method: "GET",
              credentials: "include",
              headers: {
                "Content-Type": "application/json",
              },
            })
            const json = await response.json()
            /*
              Example of json:
              {
                  "id": "user_01J8M9MBE14VGDPPJ5YZ3VG94S",
                  "first_name": "Admin",
                  "last_name": "Test1",
                  "email": "admin@medusa-test.com",
                  "avatar_url": null,
                  "metadata": {
                    "role": "product-manager"
                  },
                  "created_at": "2024-09-25T09:54:05.889Z",
                  "updated_at": "2025-07-18T10:27:34.615Z",
                  "deleted_at": null
              }
            */
        
          if (json.user.metadata.role === 'product-manager') {
            return new Map<string, string[]>([
              ["@customers", ["forbid"]],
              ["@orders", ["forbid"]],
              ["@price-lists", ["forbid"]],
              ["@promotions", ["forbid"]],
            ]);
          }
          return new Map<string, string[]>();
        },
        evaluateAccess: (permissions?: string[]) => {
          if (permissions?.includes("forbid") ) {
            return false;
          }
          return true;
        },
      }}/>
    </React.StrictMode>
  );
}

bootstrap();