permissionforge-react
v1.2.0
Published
Custom React and TypeScript RBAC SDK with role, permission, menu, route, field, action, and audit guards for admin panels.
Maintainers
Keywords
Readme
permissionforge-react
Production-ready role and permission based access control for React admin panels, CRM, ERP, BSG Backoffice, internal dashboards, and enterprise applications.
Built with React 18/19 compatibility, TypeScript, Vite library mode, ESM/CJS output, and tree-shakable exports.
Official website: https://bsgtechnologies.com - Visit here to meet, learn, contribute, discuss new topics, and connect with BSG Technologies.
Developer note: Pradeep Kumar Sheoran (Developer) is open to a job change. You can use his React.js, React Native, Android Java, Node.js packages, TypeScript scripts, and newly launched custom utilities. For feature requests or updates, leave a comment or connect through the official website.
Features
- Show or hide UI with
<Can /> - Disable buttons with
<DisableWhenNoAccess /> - Protect pages with
<RouteGuard /> - Filter sidebars and nested menus
- Guard sensitive sync or async actions
- Support roles, permissions,
anyandallmatching - Optional wildcard permissions such as
user.* - Optional case-insensitive role and permission checks
- Deny rules with
deniedPermissionanddeniedRole - Permission-or-role rules with
condition="or" - Permission groups for reusable bundles such as
user.manage - Tenant, organization, ownership, and resource-based rules
- Field-level permissions with
<FieldGuard /> - Custom policy callbacks with
when - Audit logging through
onAccessDenied - Debug-friendly decisions with
getAccessDecision - Async permission loading state in
AccessProvider - HOC support through
withAccess - Render-prop support in
<Can /> - Nested route config filtering with
filterRoutesByAccess - Typed permission builders with
permission()andcreatePermissionMap() - Role hierarchy and permission aliases for enterprise migrations
- Feature flag, environment, and time-window access rules
- Policy presets:
ownsResource,sameTenant,underApprovalLimit - Permission snapshots for debugging and support
- Menu transform mode to disable denied items with reasons
- Testing helpers and mock users through
permissionforge-react/testing - Super admin override with
user.isSuperAdminor configured roles - Router-agnostic redirect fallback
- Custom router navigation support
- Non-React access controller for services and callbacks
- Strong TypeScript autocomplete
- Lightweight package with no runtime dependency beyond React
Install
npm install permissionforge-reactyarn add permissionforge-reactpnpm add permissionforge-reactQuick Start
import { AccessProvider, Can } from "permissionforge-react";
const user = {
id: 1,
name: "Admin User",
roles: ["admin"],
permissions: ["user.create", "user.edit", "dashboard.view"],
isSuperAdmin: false
};
export function App() {
return (
<AccessProvider
user={user}
superAdminRoles={["superadmin", "root"]}
roleHierarchy={{ admin: ["manager", "viewer"], manager: ["viewer"] }}
defaultOptions={{ allowWildcard: true }}
>
<Can permission="user.create">
<button>Create User</button>
</Can>
</AccessProvider>
);
}Access Rules
Rules can use permission, role, or both. When both are provided, both must pass.
<Can permission={["user.create", "user.edit"]} match="any">
<button>Manage User</button>
</Can>
<Can permission={["user.create", "user.edit"]} match="all">
<button>Full User Access</button>
</Can>
<Can role="admin">
<section>Admin Panel</section>
</Can>
<Can permission="user.delete" fallback={<span>No Access</span>}>
<button>Delete User</button>
</Can>Advanced rules remain optional:
<Can permission="user.*">
<button>Manage Users</button>
</Can>
<Can permission="reports.view" deniedRole="suspended">
<Reports />
</Can>
<Can permission="billing.view" role="finance-admin" condition="or">
<Billing />
</Can>Render-prop mode keeps layout visible while still exposing the decision:
<Can permission="user.create">
{(allowed) => <button disabled={!allowed}>Create User</button>}
</Can>Permission Groups
Create named bundles once and reuse them across components, routes, menus, and actions.
const permissionGroups = {
"user.manage": ["user.create", "user.edit", "user.delete"],
"invoice.manage": ["invoice.view", "invoice.approve"]
};
<AccessProvider user={user} permissionGroups={permissionGroups}>
<Can permissionGroup="user.manage" match="all">
<button>Manage Users</button>
</Can>
</AccessProvider>;Tenant, Ownership, and Resource Rules
Useful for SaaS, CRM, ERP, branch-based systems, and BSG-style backoffice apps.
const user = {
id: 7,
roles: ["manager"],
permissions: ["invoice.approve"],
tenantIds: ["delhi-branch"]
};
const invoice = {
id: "INV-101",
tenantId: "delhi-branch",
ownerId: 7,
amount: 75000
};
<Can
permission="invoice.approve"
resource={invoice}
when={({ resource }) => Number(resource?.amount ?? 0) <= 100000}
>
<button>Approve Invoice</button>
</Can>;By default, resource checks use tenantId and ownerId. Use tenantKey and ownerKey when your API uses different field names.
For sensitive resources, require the metadata so a missing API field cannot silently allow access:
<Can permission="invoice.approve" resource={invoice} requireTenant requireOwner>
<button>Approve Invoice</button>
</Can>Enable strict configuration checks to deny unknown permission groups. You can also make explicit deny rules override super-admin access and evaluate day/hour rules in UTC:
<AccessProvider
user={user}
permissionGroups={permissionGroups}
defaultOptions={{
strict: true,
superAdminMode: "respect-explicit-deny",
timeZone: "utc"
}}
>
<App />
</AccessProvider>Reliability behavior:
| Feature | Configuration | Result |
| --- | --- | --- |
| Unknown permission group | strict: true | Denied with UNKNOWN_PERMISSION_GROUP |
| Required tenant metadata | requireTenant | Missing tenant denied with TENANT_MISSING |
| Required owner metadata | requireOwner | Missing owner or user ID denied with OWNER_MISSING |
| User-owned wildcard | allowWildcard: true | user.* satisfies user.create |
| Alias and role cycles | Automatic | Resolves safely without infinite recursion |
| Invalid time input | Automatic | Denied with INVALID_TIME_VALUE |
| Deterministic time rules | timeZone: "utc" | UTC day/hour evaluation |
| Thrown custom policy | Automatic | Denied with POLICY_ERROR |
| Super-admin deny precedence | superAdminMode: "respect-explicit-deny" | Explicit deny is evaluated first |
Detailed setup and response handling are covered in the Implementation Guide, Customization Guide, and Response Guide.
Field-Level Permissions
import { FieldGuard } from "permissionforge-react";
<FieldGuard field="salary" fallback={<span>Hidden</span>}>
<SalaryValue />
</FieldGuard>;field="salary" checks salary.view.
Audit Logging and Debug Decisions
<AccessProvider
user={user}
onAccessDenied={({ user, source, decision }) => {
console.warn("Denied", user?.id, source, decision.reasons);
}}
>
<Can permission="settings.update" source="settings-save-button">
<button>Save Settings</button>
</Can>
</AccessProvider>Use getAccessDecision when you need reasons for logs, tests, or debugging:
import { getAccessDecision } from "permissionforge-react";
const decision = getAccessDecision(user, { permission: "settings.update" });
console.log(decision.allowed, decision.missingPermissions, decision.reasons);Async Permission Loading
<AccessProvider user={user} loading={isLoading} loadingFallback={<Spinner />}>
<App />
</AccessProvider>Disable Button Instead of Hiding
import { DisableWhenNoAccess } from "permissionforge-react";
<DisableWhenNoAccess permission="user.delete">
<button>Delete User</button>
</DisableWhenNoAccess>;If the user is denied, the child element is cloned with disabled={true} and aria-disabled={true}. If you pass fallback, the fallback is rendered instead.
Hooks
import { useAccess, useCanAccess, usePermission, useRole } from "permissionforge-react";
function Toolbar() {
const { user, isSuperAdmin, hasPermission, hasRole, canAccess } = useAccess();
const canCreate = useCanAccess({ permission: "user.create" });
const hasCreatePermission = usePermission("user.create");
const isAdmin = useRole("admin");
return canCreate ? <button>Create</button> : null;
}Hooks must be used inside AccessProvider. If not, permissionforge-react throws a clear error.
Route Guard
import { RouteGuard } from "permissionforge-react";
<RouteGuard permission="dashboard.view" redirectTo="/unauthorized">
<Dashboard />
</RouteGuard>;RouteGuard does not require react-router-dom. When redirectTo is provided and access is denied, it uses window.location.assign(redirectTo). You can also pass a router-specific navigation function.
const navigate = useNavigate();
<RouteGuard permission="dashboard.view" redirectTo="/unauthorized" navigate={navigate}>
<Dashboard />
</RouteGuard>;You can also pass a router-specific unauthorized component through fallback.
For nested route config filtering:
import { filterRoutesByAccess } from "permissionforge-react";
const routes = [
{ path: "/dashboard", permission: "dashboard.view" },
{ path: "/settings", role: "superadmin" }
];
const allowedRoutes = filterRoutesByAccess(routes, user, {
superAdminRoles: ["superadmin"]
});<RouteGuard permission="reports.view" fallback={<Unauthorized />}>
<Reports />
</RouteGuard>Sidebar Menu Filtering
import { filterMenuByAccess } from "permissionforge-react";
const menu = [
{
label: "Dashboard",
path: "/dashboard",
permission: "dashboard.view"
},
{
label: "Users",
path: "/users",
permission: "user.view",
children: [
{
label: "Create User",
path: "/users/create",
permission: "user.create"
}
]
}
];
const filteredMenu = filterMenuByAccess(menu, user, ["superadmin"]);Parent items are kept when they have accessible children, even if the parent rule itself fails.
Wildcard menu checks can be enabled per call:
const filteredMenu = filterMenuByAccess(menu, user, ["superadmin"], {
allowWildcard: true
});Action Guard
import { guardAction } from "permissionforge-react";
await guardAction({
permission: "user.delete",
user,
action: () => deleteUser(id),
onDenied: () => alert("You do not have permission")
});BSG-branded alias and callback types are also exported:
import type { BsgAccessCallback, BsgDeniedCallback } from "permissionforge-react";
import { bsgGuardAction } from "permissionforge-react";Non-React Usage
Use createAccessController inside API clients, callbacks, or service modules.
import { createAccessController } from "permissionforge-react";
const access = createAccessController(user, {
superAdminRoles: ["superadmin"],
allowWildcard: true
});
if (access.canAccess({ permission: "invoice.*" })) {
await syncInvoices();
}HOC Usage
import { withAccess } from "permissionforge-react";
const SecureDeleteButton = withAccess(DeleteButton, {
permission: "user.delete",
fallback: null,
source: "delete-user-button"
});BSG Backoffice Example
const bsgUser = {
id: "BO-1024",
name: "Operations Admin",
roles: ["operations-admin"],
permissions: [
"dashboard.view",
"customer.view",
"customer.update",
"payment.reconcile"
]
};
<AccessProvider user={bsgUser} superAdminRoles={["superadmin"]}>
<Can permission="customer.update">
<button>Update Customer</button>
</Can>
</AccessProvider>;More Advanced Features
Typed permission builders:
import { createPermissionMap, permission } from "permissionforge-react";
const permissions = createPermissionMap(["user.create", "user.edit"] as const);
const createUser = permission("user", "create");Permission aliases and role hierarchy:
<AccessProvider
user={user}
permissionAliases={{ "user.add": "user.create" }}
roleHierarchy={{ admin: ["manager"], manager: ["viewer"] }}
>
<Can permission="user.add">Create</Can>
<Can role="viewer">Inherited Role Content</Can>
</AccessProvider>Alias chains and cyclic alias or role configurations are resolved safely without infinite recursion.
Feature flag, environment, and time-window rules:
<AccessProvider
user={user}
featureFlags={{ newBilling: true }}
defaultOptions={{
environment: "production"
}}
>
<Can
permission="billing.view"
feature="newBilling"
environment="production"
validFrom="2026-01-01"
validUntil="2026-12-31"
allowedDays={[1, 2, 3, 4, 5]}
>
<Billing />
</Can>
</AccessProvider>Policy presets:
import { ownsResource, sameTenant, underApprovalLimit } from "permissionforge-react";
<Can resource={invoice} when={ownsResource()}>
<button>Edit Own Invoice</button>
</Can>
<Can resource={invoice} when={sameTenant()}>
<button>Open Branch Invoice</button>
</Can>
<Can resource={invoice} when={underApprovalLimit(100000)}>
<button>Approve</button>
</Can>Reason and boundary components:
import { AccessBoundary, AccessReason } from "permissionforge-react";
<AccessBoundary permission="reports.view" fallback={<Unauthorized />}>
<Reports />
</AccessBoundary>
<AccessReason permission="settings.update">
{(reasons) => <p>{reasons.join(", ")}</p>}
</AccessReason>Menu transform mode:
import { transformMenuByAccess } from "permissionforge-react";
const menuWithDisabledItems = transformMenuByAccess(menu, user, {
deniedMode: "disable"
});Snapshots:
import { createAccessSnapshot } from "permissionforge-react";
const snapshot = createAccessSnapshot(user, { permissionGroups });Testing helpers:
import { mockUsers, renderWithAccess } from "permissionforge-react/testing";
renderWithAccess(<MyButton />, {
user: mockUsers.admin
});Available mock users: superadmin, admin, manager, viewer, denied.
Security reminder: permissionforge-react is a frontend UX guard. Always enforce authorization on the backend too.
Advanced v1.2 SDK
Permission Forge 1.2 adds an enterprise policy layer while keeping every existing v1.x API compatible.
import {
createAccessCache,
createPermissionForge,
defineAccessConfig
} from "permissionforge-react/core";
const config = defineAccessConfig({
version: "billing-2026.1",
policies: {
approveInvoice: {
name: "approveInvoice",
version: "billing-2026.1",
priority: 100,
effect: "allow",
rule: {
allOf: [
{ permission: "invoice.approve" },
{ role: ["manager", "finance-head"] },
{ not: { deniedRole: "suspended" } }
]
}
}
}
});
const forge = createPermissionForge({
...config,
permissions: ["invoice.view", "invoice.approve"] as const,
roles: ["manager", "finance-head", "suspended"] as const,
cache: createAccessCache({ ttlMs: 30000 })
});
forge.can(user, forge.policy("approveInvoice", "billing-2026.1"));Advanced capabilities:
- Recursive
allOf,anyOf,oneOf, andnotpolicies with cycle and depth protection. - Named, versioned, prioritized allow/deny policies and conflict resolution.
- Typed SDK factory with permission, role, and feature autocomplete.
- Async and batch decisions with concurrency, cancellation, timeout, and failure mode controls.
- Immutable field masking and row-level resource filtering.
- Bounded TTL decision cache that bypasses callback policies.
- Decision IDs, correlation IDs, redacted evaluation traces, and custom audit adapters.
- IP, device, location, session, tenant, owner, environment, feature, and time conditions.
- Headless config validation and
AccessInspectorprimitives for building a policy studio. - Dedicated
core,node,react, andreact-nativepackage entrypoints.
import { canAccess } from "permissionforge-react/core";
import { createPermissionForge } from "permissionforge-react/node";
import { AccessProvider, Can } from "permissionforge-react/react";
import { AccessProvider as NativeAccessProvider } from "permissionforge-react/react-native";See the Advanced SDK Guide for complete examples and behavior contracts.
For a function-by-function explanation, see the API Reference.
API
Components
AccessProvider
<AccessProvider user={user} superAdminRoles={["superadmin"]}>
<App />
</AccessProvider>Can
permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
tenantId?: string | number;
tenantKey?: string;
ownerId?: string | number;
ownerKey?: string;
field?: string;
when?: AccessPolicy;
fallback?: ReactNode;
children: ReactNode | ((allowed: boolean) => ReactNode);DisableWhenNoAccess
permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
field?: string;
fallback?: ReactNode;
children: ReactElement | ((allowed: boolean) => ReactElement);RouteGuard
permission?: string | string[];
permissionGroup?: string | string[];
role?: string | string[];
deniedPermission?: string | string[];
deniedRole?: string | string[];
match?: "any" | "all";
condition?: "and" | "or";
resource?: AccessResource;
field?: string;
redirectTo?: string;
navigate?: (to: string) => void;
fallback?: ReactNode;
children: ReactNode;Utilities
hasPermission(user, permission, options?)
hasRole(user, role, options?)
isSuperAdmin(user, superAdminRoles?)
canAccess(user, accessRule, options?)
getAccessDecision(user, accessRule, options?)
filterMenuByAccess(menu, user, superAdminRoles?)
filterRoutesByAccess(routes, user, options?)
guardAction(options)
bsgGuardAction(options)
createAccessController(user, options?)Types
AccessUser
AccessRule
AccessDecision
AccessDeniedEvent
AccessPolicy
AccessResource
AccessCondition
MatchType
MenuItem
PermissionGroups
RouteItem
CanProps
RouteGuardProps
AccessProviderProps
GuardActionOptions
BsgAccessCallback
BsgDeniedCallback
WithAccessOptionsAccessUser is flexible and accepts custom fields:
type AccessUser = {
id?: string | number;
name?: string;
roles?: string[];
permissions?: string[];
isSuperAdmin?: boolean;
[key: string]: unknown;
};Development
npm install
npm run typecheck
npm test
npm run build
npm run devnpm run dev starts Storybook for isolated component development.
To run the live Vite demo:
cd examples/demo-app
npm install
npm run devThe demo imports permissionforge-react from the local package through file:../.., so it shows exactly how an app connects to this SDK during development.
Additional checks:
npm run api:check
npm run size
npm run verifyAdditional Docs
- Features
- Implementation Guide
- Live Demo Guide
- Customization Guide
- Advanced SDK Guide
- API Reference
- Response Guide
- Keywords and Hashtags
- Donation
- Copyright and Ownership
- FAQ
- Migration Guide
- Architecture
- Permission Naming Guide
- Next.js and SSR Guide
- Security Policy
- Contributing
- Changelog
Hindi Summary
permissionforge-react ek lightweight React + TypeScript access-control library hai. Isse aap admin panel, CRM, ERP, dashboard, ya BSG Backoffice me role aur permission ke basis par buttons, routes, menu items, fields, aur actions ko show, hide, disable, ya guard kar sakte hain. Backend authorization zaroor enforce karein; frontend guard sirf UX safety ke liye hota hai.
Publish
npm version patch
npm run build
npm publish --access publicFor npm provenance:
npm publish --access public --provenanceThe package publishes dist, README, license, changelog, security, contributing, FAQ, migration, docs, features, donation, copyright, and examples files.
Support
Developer: Pradeep Kumar Sheoran (Developer)
Company: BSG Technologies
Contact / WhatsApp: +91-8595147850
Website: https://bsgtechnologies.com
For support and a cup of coffee, visit BSG Technologies.
Donation / coffee support: UPI on mobile number +91-8595147850
This SDK is maintained as custom-owned source code for Permission Forge. It has no runtime dependency beyond React peer integration, and every permission feature is implemented inside this package.
Keywords
#React #TypeScript #RBAC #Permissions #AdminPanel #CRM #ERP #Dashboard #BSGBackoffice #BSGTechnologies
