JSPM

secure-role-guard

1.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 20
  • Score
    100M100P100Q44179F
  • License MIT

Zero-dependency RBAC authorization library for React & Node.js. Role-based access control with permissions, middleware for Express/Next.js. Define roles once, use everywhere.

Package Exports

  • secure-role-guard
  • secure-role-guard/adapters
  • secure-role-guard/adapters/express
  • secure-role-guard/adapters/nextjs
  • secure-role-guard/core
  • secure-role-guard/react

Readme

secure-role-guard

Zero-dependency RBAC authorization for React & Node.js. Define roles once, use everywhere.

npm version TypeScript Zero Dependencies Tests & CI

Author: Sohel Rahaman


🚀 Quick Overview

Custom Code (Without Package) With secure-role-guard
Write permission logic everywhere Define once, use everywhere
Handle null/undefined edge cases Built-in, tested
Different code for frontend/backend Same API everywhere
2-4 hours setup 10 minutes setup

📦 Installation

npm install secure-role-guard
# or
pnpm add secure-role-guard
# or
yarn add secure-role-guard

📖 Usage Guide

Choose your setup:

I want to use in... Jump to
React/Next.js only Frontend Only
Express/Node.js only Backend Only
Both Frontend + Backend Full Stack

Frontend Only

Step 1: Define Roles

// lib/roles.ts
import { defineRoles } from "secure-role-guard";

export const roleRegistry = defineRoles({
  admin: ["user.create", "user.read", "user.update", "user.delete"],
  manager: ["user.read", "user.update"],
  viewer: ["user.read"],
});

Step 2: Setup Provider

// app/providers.tsx (Next.js App Router)
// or src/App.tsx (Vite/CRA)
"use client";

import { PermissionProvider } from "secure-role-guard/react";
import { roleRegistry } from "@/lib/roles";

export function Providers({ children, user }) {
  // user = { roles: ['admin'], permissions: [] } from your auth
  return (
    <PermissionProvider user={user} registry={roleRegistry}>
      {children}
    </PermissionProvider>
  );
}

Step 3: Use in Components

import { Can, useCan } from "secure-role-guard/react";

function Dashboard() {
  const canDelete = useCan("user.delete");

  return (
    <div>
      {/* Method 1: Component */}
      <Can permission="user.create">
        <button>Add User</button>
      </Can>

      <Can permission="user.update">
        <button>Edit User</button>
      </Can>

      {/* Method 2: Hook */}
      {canDelete && <button>Delete User</button>}

      {/* With fallback */}
      <Can permission="admin.access" fallback={<p>Access Denied</p>}>
        <AdminPanel />
      </Can>

      {/* Multiple permissions (ANY) */}
      <Can permissions={["user.update", "user.delete"]} anyOf>
        <UserActions />
      </Can>
    </div>
  );
}

That's it for frontend!


Backend Only

Step 1: Define Roles

// lib/roles.ts
import { defineRoles } from "secure-role-guard/core";

export const roleRegistry = defineRoles({
  admin: ["user.create", "user.read", "user.update", "user.delete"],
  manager: ["user.read", "user.update"],
  viewer: ["user.read"],
});

Step 2: Use in Express

// server.ts
import express from "express";
import { requirePermission } from "secure-role-guard/adapters/express";
import { roleRegistry } from "./lib/roles";

const app = express();

// Your auth middleware (sets req.user)
app.use(yourAuthMiddleware);

// Protected routes
app.get(
  "/api/users",
  requirePermission("user.read", roleRegistry),
  (req, res) => {
    res.json({ users: [] });
  },
);

app.post(
  "/api/users",
  requirePermission("user.create", roleRegistry),
  (req, res) => {
    res.json({ created: true });
  },
);

app.delete(
  "/api/users/:id",
  requirePermission("user.delete", roleRegistry),
  (req, res) => {
    res.json({ deleted: true });
  },
);

Manual Check (Without Middleware)

import { canUser } from "secure-role-guard/core";

app.put("/api/users/:id", (req, res) => {
  if (!canUser(req.user, "user.update", roleRegistry)) {
    return res.status(403).json({ error: "Forbidden" });
  }
  // ... your logic
});

That's it for backend!


Full Stack

Use same role definitions for both:

// shared/roles.ts (shared between frontend & backend)
import { defineRoles } from "secure-role-guard";

export const roleRegistry = defineRoles({
  admin: ["*"], // Full access
  manager: ["user.read", "user.update", "report.*"],
  support: ["ticket.read", "ticket.reply"],
  viewer: ["user.read"],
});

Frontend: Follow Frontend Only steps
Backend: Follow Backend Only steps

💡 Pro tip: Keep roles in a shared package or copy to both projects.


📚 API Reference

Core Functions

Function Description
defineRoles(roles) Create role registry
canUser(user, permission, registry) Check single permission
canUserAll(user, permissions, registry) Check ALL permissions
canUserAny(user, permissions, registry) Check ANY permission

React Components

Component Description
<PermissionProvider> Wrap your app
<Can permission="..."> Show if allowed
<Cannot permission="..."> Show if NOT allowed

React Hooks

Hook Returns
useCan(permission) boolean
useCanAll(permissions) boolean
useCanAny(permissions) boolean

User Context Shape

const user = {
  userId: "user-123", // Optional
  roles: ["admin", "manager"], // Role names
  permissions: ["custom.perm"], // Direct permissions (bypass roles)
  meta: { tenantId: "..." }, // Optional metadata
};

Wildcard Permissions

Pattern Grants
* Everything
user.* user.read, user.update, etc.
report.admin.* report.admin.view, report.admin.export, etc.

🔄 Dynamic Roles (From Database)

If admin creates roles at runtime:

// Fetch roles from your database
const rolesFromDB = await fetchRolesFromDB();

// Transform to: { roleName: ['permission1', 'permission2'] }
const roleDefinition = {};
rolesFromDB.forEach((role) => {
  roleDefinition[role.name] = role.permissions;
});

// Create registry
const registry = defineRoles(roleDefinition);

Works with any database: MongoDB, PostgreSQL, MySQL, SQLite, etc.


⚠️ Important Notes

This Package Does NOT:

  • ❌ Handle authentication (JWT, sessions, cookies)
  • ❌ Make API/database calls
  • ❌ Store global state

You provide: User with roles → We check: Permissions

Security

  • ✅ Deny by default (undefined = false)
  • ✅ Zero dependencies in core
  • ✅ Immutable configurations
  • ✅ Pure functions (no side effects)

🔒 Backward Compatibility

Version Meaning
1.0.x → 1.0.y Bug fixes, safe to update
1.x.0 → 1.y.0 New features, no breaking changes
1.x.x → 2.0.0 Breaking changes, migration guide provided

Promise: v1.x APIs will never break. Update with confidence.


📄 License

MIT © Sohel Rahaman