rbac-shield 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RBAC Shield Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # πŸ” RBAC Shield
2
+
3
+ [![npm version](https://img.shields.io/npm/v/rbac-shield.svg)](https://www.npmjs.com/package/rbac-shield)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
+ [![Downloads](https://img.shields.io/npm/dt/rbac-shield.svg)](https://www.npmjs.com/package/rbac-shield)
7
+
8
+ **The production-ready, type-safe Role-Based Access Control (RBAC) system for Next.js applications.**
9
+
10
+ Built for modern web development with **React 19**, **TypeScript 5**, and **Next.js App Router** compatibility. RBAC Shield provides a seamless, multi-tenant permission system that just works.
11
+
12
+ ---
13
+
14
+ ## πŸ“‘ Table of Contents
15
+
16
+ - [Features](#-features)
17
+ - [Quick Setup (CLI)](#-quick-setup-recommended)
18
+ - [Manual Installation](#-manual-installation)
19
+ - [Quick Start](#-quick-start)
20
+ - [API Reference](#-api-reference)
21
+ - [Advanced Usage](#-advanced-usage)
22
+ - [Security & Best Practices](#-security--best-practices)
23
+ - [Troubleshooting](#-troubleshooting)
24
+
25
+ ---
26
+
27
+ ## ✨ Features
28
+
29
+ - 🎯 **Type-Safe Permissions**: Full TypeScript support with intelligent autocomplete for your specific resources and actions.
30
+ - πŸš€ **High Performance**: Optimized with React Context and memoization. Permission checks are < 1ms.
31
+ - 🏒 **Multi-Tenant Native**: Switch between multiple organizations/roles instantly without page reloads.
32
+ - πŸ›‘οΈ **Route Protection**: Declarative client-side guards with automatic handling of loading states and redirects.
33
+ - πŸ”„ **SSR & Hydration Safe**: Built from the ground up for Next.js App Routerβ€”no hydration mismatches.
34
+ - 🌍 **Universal Support**: Works seamlessly in **Client Components**, **Server Components**, and **Middleware**.
35
+ - πŸ“¦ **Zero Dependencies**: Lightweight (~35KB) and built entirely on standard React APIs.
36
+
37
+ ---
38
+
39
+ ## πŸš€ Quick Setup (Recommended)
40
+
41
+ The fastest way to integrate RBAC Shield is with our interactive CLI. It initializes your configuration and handles all boilerplate.
42
+
43
+ ```bash
44
+ npx rbac-shield init
45
+ ```
46
+
47
+ The CLI will:
48
+
49
+ 1. Detect your project type (Next.js/React, TS/JS)
50
+ 2. Help you define your resources (e.g., `projects`) and actions (e.g., `create`)
51
+ 3. Generate a clean, type-safe `lib/rbac.ts` file configured for your app
52
+
53
+ ---
54
+
55
+ ## πŸ“¦ Manual Installation
56
+
57
+ If you prefer to set things up yourself:
58
+
59
+ ```bash
60
+ npm install rbac-shield
61
+ # or
62
+ yarn add rbac-shield
63
+ # or
64
+ pnpm add rbac-shield
65
+ # or
66
+ bun add rbac-shield
67
+ ```
68
+
69
+ **Peer Dependencies:**
70
+ Ensure you have peer dependencies installed (standard in Next.js apps):
71
+ `react >= 18.0.0`, `react-dom >= 18.0.0`, `next >= 13.0.0`
72
+
73
+ ---
74
+
75
+ ## πŸš€ Quick Start
76
+
77
+ ### 1. Define Your Schema
78
+
79
+ Create a single source of truth for your permissions in `lib/rbac.ts` (or `config/rbac.ts`).
80
+
81
+ ```typescript
82
+ // lib/rbac.ts
83
+ import { createRBAC } from "rbac-shield";
84
+
85
+ // 1. Define resources (things you secure)
86
+ export type Resources = "projects" | "billing" | "users" | "marketing";
87
+
88
+ // 2. Define actions (what users can do)
89
+ export type Actions = "view" | "create" | "edit" | "delete" | "export";
90
+
91
+ // 3. Create your instances
92
+ export const {
93
+ RBACProvider,
94
+ useStore,
95
+ useHasPermission,
96
+ useHasAnyPermission,
97
+ useHasAllPermissions,
98
+ usePermissions,
99
+ Can,
100
+ ProtectedRoute,
101
+ } = createRBAC<Resources, Actions>();
102
+ ```
103
+
104
+ ### 2. Wrap Your App
105
+
106
+ Add the provider to your root layout to enable state management.
107
+
108
+ ```tsx
109
+ // app/layout.tsx
110
+ import { RBACProvider } from "@/lib/rbac";
111
+
112
+ export default function RootLayout({
113
+ children,
114
+ }: {
115
+ children: React.ReactNode;
116
+ }) {
117
+ return (
118
+ <html lang="en">
119
+ <body>
120
+ <RBACProvider>{children}</RBACProvider>
121
+ </body>
122
+ </html>
123
+ );
124
+ }
125
+ ```
126
+
127
+ ### 3. Load Permissions
128
+
129
+ Initialize the system with data from your backend (e.g., after login).
130
+
131
+ ```tsx
132
+ // components/AuthProvider.tsx
133
+ "use client";
134
+ import { useEffect } from "react";
135
+ import { useStore } from "@/lib/rbac";
136
+
137
+ export function AuthProvider({
138
+ user,
139
+ children,
140
+ }: {
141
+ user: any;
142
+ children: React.ReactNode;
143
+ }) {
144
+ const { setAuth, switchTenant } = useStore();
145
+
146
+ useEffect(() => {
147
+ if (user) {
148
+ // Load permissions into RBAC Shield
149
+ setAuth([
150
+ {
151
+ tenantId: "team-123",
152
+ permissions: ["projects:view", "projects:edit"],
153
+ },
154
+ ]);
155
+ // Activate the context
156
+ switchTenant("team-123");
157
+ }
158
+ }, [user, setAuth, switchTenant]);
159
+
160
+ return <>{children}</>;
161
+ }
162
+ ```
163
+
164
+ ### 4. Secure Your App
165
+
166
+ Use the generated hooks and components anywhere.
167
+
168
+ ```tsx
169
+ import { ProtectedRoute, Can, useHasPermission } from "@/lib/rbac";
170
+
171
+ export default function ProjectSettings() {
172
+ const canDelete = useHasPermission("projects:delete");
173
+
174
+ return (
175
+ // 1. Protect Example: Redirects to / if missing permission
176
+ <ProtectedRoute requiredPermission="projects:edit" fallbackPath="/">
177
+ <h1>Project Settings</h1>
178
+
179
+ {/* 2. Conditional Render Example */}
180
+ <Can permission="billing:view" fallback={<p>Upgrade to see billing</p>}>
181
+ <BillingWidget />
182
+ </Can>
183
+
184
+ {/* 3. Hook Logic Example */}
185
+ <button disabled={!canDelete}>Delete Project</button>
186
+ </ProtectedRoute>
187
+ );
188
+ }
189
+ ```
190
+
191
+ ---
192
+
193
+ ## πŸ“š API Reference
194
+
195
+ ### `<ProtectedRoute>`
196
+
197
+ A wrapper component that guards an entire route or section.
198
+
199
+ - **requiredPermission**: `Resources:Actions` string (e.g., `billing:view`).
200
+ - **fallbackPath**: (Optional) URL to redirect to if unauthorized. Default: `/`.
201
+ - **fallback**: (Optional) React Node to render instead of redirecting.
202
+
203
+ ### `<Can>`
204
+
205
+ A structural component for showing/hiding UI elements.
206
+
207
+ - **permission**: The required permission string.
208
+ - **fallback**: (Optional) UI to show when permission is denied.
209
+
210
+ ### `useHasPermission(permission)`
211
+
212
+ Returns `boolean`. Checks for exact permission match or wildcard `*`.
213
+
214
+ ### `useHasAnyPermission([perm1, perm2])`
215
+
216
+ Returns `boolean`. True if user has **at least one** of the listed permissions.
217
+
218
+ ### `useHasAllPermissions([perm1, perm2])`
219
+
220
+ Returns `boolean`. True only if user has **every single** listed permission.
221
+
222
+ ### `useStore()`
223
+
224
+ Access raw state and actions:
225
+
226
+ - `isLoading`: `boolean`
227
+ - `activeTenantId`: `string | null`
228
+ - `switchTenant(id)`: Change active context
229
+ - `setAuth(authData)`: Load permission data
230
+
231
+ ---
232
+
233
+ ## οΏ½ Security & Best Practices
234
+
235
+ > [!IMPORTANT] > **Client-side checks are for User Experience (UX) only.**
236
+
237
+ RBAC Shield controls what the user _sees_ in the browser, but it cannot stop a determined attacker from crafting API requests manually.
238
+
239
+ ### 1. Server-Side Verification (Universal)
240
+
241
+ Use the universal `checkPermission` helper in Middleware, Server Actions, or API Routes:
242
+
243
+ ```tsx
244
+ import { checkPermission } from "rbac-shield";
245
+ // 1. Middleware Example
246
+ export function middleware(req) {
247
+ const permissions = getPermissionsFromCookie(req);
248
+ if (!checkPermission(permissions, "admin:access")) {
249
+ return NextResponse.redirect(new URL("/403", req.url));
250
+ }
251
+ }
252
+
253
+ // 2. Server Action Example
254
+ export async function deleteProject() {
255
+ const user = await getCurrentUser();
256
+ if (!checkPermission(user.permissions, "projects:delete")) {
257
+ throw new Error("Unauthorized");
258
+ }
259
+ }
260
+ ```
261
+
262
+ ### 2. Debug Mode
263
+
264
+ Troubleshooting permissions is easier with debug mode enabled.
265
+
266
+ ```tsx
267
+ <RBACProvider debug={true}>{children}</RBACProvider>
268
+ ```
269
+
270
+ This logs helpful messages to the console:
271
+
272
+ - `[RBAC Shield] Permissions loaded for tenants: ['org_1']`
273
+ - `[RBAC Shield] Denied: Required 'billing:edit', User has ['projects:view']`
274
+
275
+ ### 3. Production Checklist
276
+
277
+ 1. **API Validation**: Every API route and Server Action MUST verify permissions independently on the server using `checkPermission()`.
278
+ 2. **Middleware**: Use Next.js Middleware to protect route segments (`/admin/*`) before they even render.
279
+ 3. **RBAC Shield**: Use this library's components (Client-Side) to hide UI elements for a smooth experience.
280
+
281
+ ---
282
+
283
+ ## πŸ› Troubleshooting
284
+
285
+ | Issue | Solution |
286
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------- |
287
+ | **Infinite Loading** | Ensure `RBACProvider` wraps your app and `setAuth` is called with valid data. |
288
+ | **Type Errors** | Verify your `Resources` and `Actions` types in `lib/rbac.ts` are exported. |
289
+ | **Hydration Mismatch** | `ProtectedRoute` and `Can` are client components; ensure they are used in client contexts or wrapped properly. |
290
+
291
+ ---
292
+
293
+ ## 🀝 Contributing
294
+
295
+ We welcome contributions! Please open an issue or submit a PR on our [GitHub repository](https://github.com/your-repo/rbac-shield).
296
+
297
+ ## πŸ“„ License
298
+
299
+ MIT Β© [Arif Hossain Roman](https://github.com/your-username)
package/bin/cli.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const packageJson = require('../package.json');
6
+
7
+ program
8
+ .name('rbac-shield')
9
+ .description('Interactive CLI for RBAC Shield setup')
10
+ .version(packageJson.version);
11
+
12
+ program
13
+ .command('init')
14
+ .description('Initialize RBAC Shield in your project')
15
+ .action(async () => {
16
+ try {
17
+ const init = require('./init');
18
+ await init();
19
+ } catch (error) {
20
+ console.error(chalk.red('Error:'), error.message);
21
+ process.exit(1);
22
+ }
23
+ });
24
+
25
+ // Show help if no command provided
26
+ if (!process.argv.slice(2).length) {
27
+ program.outputHelp();
28
+ }
29
+
30
+ program.parse(process.argv);
package/bin/init.js ADDED
@@ -0,0 +1,176 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync } = require('child_process');
4
+ const prompts = require('prompts');
5
+ const chalk = require('chalk');
6
+ const { getTemplate, TEMPLATES } = require('./templates');
7
+ const {
8
+ detectFramework,
9
+ isTypeScriptProject,
10
+ ensureDirectory,
11
+ fileExists,
12
+ showNextSteps
13
+ } = require('./utils');
14
+
15
+ async function init() {
16
+ console.log(chalk.cyan.bold('\nπŸ” RBAC Shield - Interactive Setup'));
17
+ console.log(chalk.cyan('━'.repeat(40)) + '\n');
18
+
19
+ // Detect project type
20
+ const framework = detectFramework();
21
+ const hasTypeScript = isTypeScriptProject();
22
+
23
+ if (framework) {
24
+ console.log(chalk.green(`βœ“ Detected ${framework} project\n`));
25
+ }
26
+
27
+ // Check if rbac-shield is already installed
28
+ const isInstalled = fileExists(path.join(process.cwd(), 'node_modules', 'rbac-shield'));
29
+
30
+ // Prompt for language
31
+ const { language } = await prompts({
32
+ type: 'select',
33
+ name: 'language',
34
+ message: 'Use TypeScript or JavaScript?',
35
+ choices: [
36
+ { title: 'TypeScript', value: 'typescript', selected: hasTypeScript },
37
+ { title: 'JavaScript', value: 'javascript' }
38
+ ],
39
+ initial: hasTypeScript ? 0 : 1
40
+ });
41
+
42
+ if (!language) {
43
+ console.log(chalk.yellow('\nβœ– Setup cancelled'));
44
+ process.exit(0);
45
+ }
46
+
47
+ // Prompt for template
48
+ const { template } = await prompts({
49
+ type: 'select',
50
+ name: 'template',
51
+ message: 'Choose a template:',
52
+ choices: [
53
+ {
54
+ title: 'Basic (projects, users, settings)',
55
+ value: 'basic',
56
+ description: 'General purpose RBAC setup'
57
+ },
58
+ {
59
+ title: 'E-commerce (products, orders, customers)',
60
+ value: 'ecommerce',
61
+ description: 'For online stores and marketplaces'
62
+ },
63
+ {
64
+ title: 'SaaS (workspaces, billing, teams)',
65
+ value: 'saas',
66
+ description: 'For multi-tenant SaaS applications'
67
+ },
68
+ {
69
+ title: 'Custom (define your own)',
70
+ value: 'custom',
71
+ description: 'Specify your own resources and actions'
72
+ }
73
+ ],
74
+ initial: 0
75
+ });
76
+
77
+ if (!template) {
78
+ console.log(chalk.yellow('\nβœ– Setup cancelled'));
79
+ process.exit(0);
80
+ }
81
+
82
+ let resources, actions;
83
+
84
+ if (template === 'custom') {
85
+ // Prompt for custom resources and actions
86
+ const customConfig = await prompts([
87
+ {
88
+ type: 'text',
89
+ name: 'resources',
90
+ message: 'Enter resource types (comma-separated):',
91
+ initial: 'projects, users, settings',
92
+ validate: value => value.trim().length > 0 || 'Please enter at least one resource'
93
+ },
94
+ {
95
+ type: 'text',
96
+ name: 'actions',
97
+ message: 'Enter action types (comma-separated):',
98
+ initial: 'view, create, edit, delete',
99
+ validate: value => value.trim().length > 0 || 'Please enter at least one action'
100
+ }
101
+ ]);
102
+
103
+ if (!customConfig.resources || !customConfig.actions) {
104
+ console.log(chalk.yellow('\nβœ– Setup cancelled'));
105
+ process.exit(0);
106
+ }
107
+
108
+ resources = customConfig.resources.split(',').map(r => r.trim()).filter(Boolean);
109
+ actions = customConfig.actions.split(',').map(a => a.trim()).filter(Boolean);
110
+ } else {
111
+ const templateData = TEMPLATES[template];
112
+ resources = templateData.resources;
113
+ actions = templateData.actions;
114
+
115
+ // Confirm template
116
+ const { confirmed } = await prompts({
117
+ type: 'confirm',
118
+ name: 'confirmed',
119
+ message: `Use resources: ${resources.join(', ')} and actions: ${actions.join(', ')}?`,
120
+ initial: true
121
+ });
122
+
123
+ if (!confirmed) {
124
+ console.log(chalk.yellow('\nβœ– Setup cancelled'));
125
+ process.exit(0);
126
+ }
127
+ }
128
+
129
+ console.log('');
130
+
131
+ // Install rbac-shield if not installed
132
+ if (!isInstalled) {
133
+ console.log(chalk.blue('πŸ“¦ Installing rbac-shield...'));
134
+ try {
135
+ execSync('npm install rbac-shield', { stdio: 'inherit' });
136
+ console.log(chalk.green('βœ“ rbac-shield installed\n'));
137
+ } catch (error) {
138
+ console.error(chalk.red('βœ– Failed to install rbac-shield'));
139
+ console.error(chalk.yellow('Please install manually: npm install rbac-shield'));
140
+ process.exit(1);
141
+ }
142
+ }
143
+
144
+ // Create lib directory
145
+ const libDir = path.join(process.cwd(), 'lib');
146
+ ensureDirectory(libDir);
147
+
148
+ // Generate config file
149
+ const ext = language === 'typescript' ? 'ts' : 'js';
150
+ const configPath = path.join(libDir, `rbac.${ext}`);
151
+
152
+ if (fileExists(configPath)) {
153
+ const { overwrite } = await prompts({
154
+ type: 'confirm',
155
+ name: 'overwrite',
156
+ message: `${configPath} already exists. Overwrite?`,
157
+ initial: false
158
+ });
159
+
160
+ if (!overwrite) {
161
+ console.log(chalk.yellow('\nβœ– Setup cancelled'));
162
+ process.exit(0);
163
+ }
164
+ }
165
+
166
+ const configContent = getTemplate(template, language, resources, actions);
167
+ fs.writeFileSync(configPath, configContent, 'utf8');
168
+
169
+ console.log(chalk.green(`βœ“ Created lib/rbac.${ext}`));
170
+ console.log(chalk.green('βœ“ Setup complete!\n'));
171
+
172
+ // Show next steps
173
+ showNextSteps(framework, language);
174
+ }
175
+
176
+ module.exports = init;
@@ -0,0 +1,68 @@
1
+ const TEMPLATES = {
2
+ basic: {
3
+ resources: ['projects', 'users', 'settings'],
4
+ actions: ['view', 'create', 'edit', 'delete']
5
+ },
6
+ ecommerce: {
7
+ resources: ['products', 'orders', 'customers', 'inventory'],
8
+ actions: ['view', 'create', 'edit', 'delete', 'manage']
9
+ },
10
+ saas: {
11
+ resources: ['workspaces', 'billing', 'teams', 'api-keys'],
12
+ actions: ['view', 'create', 'edit', 'delete', 'manage']
13
+ }
14
+ };
15
+
16
+ function getTemplate(templateName, language, resources, actions) {
17
+ const isTypeScript = language === 'typescript';
18
+
19
+ const resourcesType = resources.map(r => `'${r}'`).join(' | ');
20
+ const actionsType = actions.map(a => `'${a}'`).join(' | ');
21
+
22
+ if (isTypeScript) {
23
+ return `import { createRBAC } from 'rbac-shield';
24
+
25
+ // Define your application's resources
26
+ export type Resources = ${resourcesType};
27
+
28
+ // Define your application's actions
29
+ export type Actions = ${actionsType};
30
+
31
+ // Initialize RBAC with your types
32
+ export const {
33
+ RBACProvider,
34
+ useStore,
35
+ useHasPermission,
36
+ useHasAnyPermission,
37
+ useHasAllPermissions,
38
+ usePermissions,
39
+ Can,
40
+ RBACErrorBoundary,
41
+ ProtectedRoute,
42
+ } = createRBAC<Resources, Actions>();
43
+ `;
44
+ } else {
45
+ return `import { createRBAC } from 'rbac-shield';
46
+
47
+ // Initialize RBAC
48
+ // Resources: ${resources.join(', ')}
49
+ // Actions: ${actions.join(', ')}
50
+ export const {
51
+ RBACProvider,
52
+ useStore,
53
+ useHasPermission,
54
+ useHasAnyPermission,
55
+ useHasAllPermissions,
56
+ usePermissions,
57
+ Can,
58
+ RBACErrorBoundary,
59
+ ProtectedRoute,
60
+ } = createRBAC();
61
+ `;
62
+ }
63
+ }
64
+
65
+ module.exports = {
66
+ TEMPLATES,
67
+ getTemplate
68
+ };