secure-role-guard 1.0.0 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +162 -647
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # secure-role-guard
2
2
 
3
- > Zero-vulnerability, framework-agnostic RBAC authorization library for React and Node.js applications.
3
+ > Zero-dependency RBAC authorization for React & Node.js. Define roles once, use everywhere.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/secure-role-guard.svg)](https://www.npmjs.com/package/secure-role-guard)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
8
7
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://www.npmjs.com/package/secure-role-guard)
9
8
 
@@ -11,73 +10,18 @@
11
10
 
12
11
  ---
13
12
 
14
- ## Table of Contents
13
+ ## 🚀 Quick Overview
15
14
 
16
- - [What This Package Does](#what-this-package-does)
17
- - [What This Package Does NOT Do](#what-this-package-does-not-do)
18
- - [Security Guarantees](#security-guarantees)
19
- - [Installation](#installation)
20
- - [Quick Start](#quick-start)
21
- - [API Reference](#api-reference)
22
- - [Real-World Examples](#real-world-examples)
23
- - [Framework Compatibility](#framework-compatibility)
24
- - [Common Mistakes to Avoid](#common-mistakes-to-avoid)
25
- - [License](#license)
15
+ | Custom Code (Without Package) | With secure-role-guard |
16
+ | ----------------------------------- | --------------------------- |
17
+ | Write permission logic everywhere | Define once, use everywhere |
18
+ | Handle null/undefined edge cases | Built-in, tested |
19
+ | Different code for frontend/backend | Same API everywhere |
20
+ | 2-4 hours setup | 10 minutes setup |
26
21
 
27
22
  ---
28
23
 
29
- ## What This Package Does ✅
30
-
31
- | Feature | Description |
32
- | ----------------------------- | ------------------------------------------------------- |
33
- | **Role-Based Access Control** | Define roles with granular permissions |
34
- | **Pure Permission Checking** | Deterministic, side-effect-free authorization |
35
- | **React Integration** | Provider, hooks, and components for any React framework |
36
- | **Backend Adapters** | Optional Express and Next.js middleware |
37
- | **Wildcard Support** | Grant access with `*` (all) or `namespace.*` patterns |
38
- | **TypeScript First** | Full type safety with strict mode |
39
- | **Zero Dependencies** | Core has zero runtime dependencies |
40
- | **Framework Agnostic** | Works with Next.js, Remix, Gatsby, Astro, Vite, CRA |
41
-
42
- ---
43
-
44
- ## What This Package Does NOT Do ❌
45
-
46
- > **CRITICAL:** This package handles **AUTHORIZATION** only, **NOT AUTHENTICATION**.
47
-
48
- | This Package Does NOT | You Must Handle This |
49
- | --------------------- | -------------------------------------- |
50
- | Parse JWT tokens | Use a JWT library (jsonwebtoken, jose) |
51
- | Verify authentication | Use Auth.js, Clerk, NextAuth, Passport |
52
- | Read cookies | Use your framework's cookie API |
53
- | Manage sessions | Use express-session, iron-session |
54
- | Make network requests | Fetch user data yourself |
55
- | Access databases | Query your DB to get user roles |
56
- | Store global state | Pass user context explicitly |
57
-
58
- ### Why?
59
-
60
- Authorization and authentication are **separate concerns**. Mixing them creates security vulnerabilities. This package focuses on **one job** and does it correctly.
61
-
62
- ---
63
-
64
- ## Security Guarantees
65
-
66
- | Guarantee | Implementation |
67
- | ------------------------ | -------------------------------------------------- |
68
- | ✅ **Deny by default** | Undefined permissions return `false` |
69
- | ✅ **Immutable configs** | Role definitions are frozen with `Object.freeze()` |
70
- | ✅ **Pure functions** | No side effects, no state mutations |
71
- | ✅ **No eval/regex** | Only strict string matching |
72
- | ✅ **Zero dependencies** | Core has zero runtime dependencies |
73
- | ✅ **TypeScript strict** | Full strict mode compilation |
74
- | ✅ **No global state** | All state is passed explicitly |
75
- | ✅ **No network calls** | Never makes HTTP requests |
76
- | ✅ **No file system** | Never reads or writes files |
77
-
78
- ---
79
-
80
- ## Installation
24
+ ## 📦 Installation
81
25
 
82
26
  ```bash
83
27
  npm install secure-role-guard
@@ -87,275 +31,122 @@ pnpm add secure-role-guard
87
31
  yarn add secure-role-guard
88
32
  ```
89
33
 
90
- **Peer Dependencies:**
34
+ ---
35
+
36
+ ## 📖 Usage Guide
37
+
38
+ Choose your setup:
91
39
 
92
- - React ≥16.8.0 (optional, only needed for React features)
40
+ | I want to use in... | Jump to |
41
+ | --------------------------- | ------------------------------- |
42
+ | **React/Next.js only** | [Frontend Only](#frontend-only) |
43
+ | **Express/Node.js only** | [Backend Only](#backend-only) |
44
+ | **Both Frontend + Backend** | [Full Stack](#full-stack) |
93
45
 
94
46
  ---
95
47
 
96
- ## Quick Start
48
+ ## Frontend Only
97
49
 
98
- ### 1. Define Your Roles
50
+ ### Step 1: Define Roles
99
51
 
100
52
  ```typescript
101
- // roles.ts
53
+ // lib/roles.ts
102
54
  import { defineRoles } from "secure-role-guard";
103
55
 
104
56
  export const roleRegistry = defineRoles({
105
- superadmin: ["*"], // Full access
106
- admin: ["user.read", "user.update", "user.delete", "report.view"],
107
- manager: ["user.read", "report.*"], // Namespace wildcard
108
- support: ["ticket.read", "ticket.reply"],
57
+ admin: ["user.create", "user.read", "user.update", "user.delete"],
58
+ manager: ["user.read", "user.update"],
109
59
  viewer: ["user.read"],
110
60
  });
111
61
  ```
112
62
 
113
- ### 2. Check Permissions (Core - No React)
114
-
115
- ```typescript
116
- import { canUser } from "secure-role-guard";
117
- import { roleRegistry } from "./roles";
118
-
119
- // Your user context (from your auth system)
120
- const user = {
121
- userId: "user-123",
122
- roles: ["admin"],
123
- permissions: ["custom.feature"], // Direct permissions
124
- };
125
-
126
- // Simple checks
127
- canUser(user, "user.update", roleRegistry); // true
128
- canUser(user, "user.delete", roleRegistry); // true
129
- canUser(user, "billing.access", roleRegistry); // false (deny by default)
130
- ```
131
-
132
- ### 3. React Integration
63
+ ### Step 2: Setup Provider
133
64
 
134
65
  ```tsx
135
- import { PermissionProvider, Can, useCan } from "secure-role-guard";
136
- import { roleRegistry } from "./roles";
66
+ // app/providers.tsx (Next.js App Router)
67
+ // or src/App.tsx (Vite/CRA)
68
+ "use client";
137
69
 
138
- // Wrap your app with the provider
139
- function App() {
140
- const user = useAuth(); // YOUR auth hook (not from this package)
70
+ import { PermissionProvider } from "secure-role-guard/react";
71
+ import { roleRegistry } from "@/lib/roles";
141
72
 
73
+ export function Providers({ children, user }) {
74
+ // user = { roles: ['admin'], permissions: [] } from your auth
142
75
  return (
143
76
  <PermissionProvider user={user} registry={roleRegistry}>
144
- <Dashboard />
77
+ {children}
145
78
  </PermissionProvider>
146
79
  );
147
80
  }
81
+ ```
82
+
83
+ ### Step 3: Use in Components
84
+
85
+ ```tsx
86
+ import { Can, useCan } from "secure-role-guard/react";
148
87
 
149
- // Use the Can component for declarative rendering
150
88
  function Dashboard() {
89
+ const canDelete = useCan("user.delete");
90
+
151
91
  return (
152
92
  <div>
93
+ {/* Method 1: Component */}
94
+ <Can permission="user.create">
95
+ <button>Add User</button>
96
+ </Can>
97
+
153
98
  <Can permission="user.update">
154
- <EditUserButton />
99
+ <button>Edit User</button>
155
100
  </Can>
156
101
 
157
- <Can permission="admin.access" fallback={<UpgradePrompt />}>
102
+ {/* Method 2: Hook */}
103
+ {canDelete && <button>Delete User</button>}
104
+
105
+ {/* With fallback */}
106
+ <Can permission="admin.access" fallback={<p>Access Denied</p>}>
158
107
  <AdminPanel />
159
108
  </Can>
160
109
 
161
- <Can permissions={["report.view", "report.export"]} anyOf>
162
- <ReportSection />
110
+ {/* Multiple permissions (ANY) */}
111
+ <Can permissions={["user.update", "user.delete"]} anyOf>
112
+ <UserActions />
163
113
  </Can>
164
114
  </div>
165
115
  );
166
116
  }
167
-
168
- // Or use hooks for programmatic checks
169
- function UserActions() {
170
- const canEdit = useCan("user.update");
171
- const canDelete = useCan("user.delete");
172
-
173
- return (
174
- <div>
175
- {canEdit && <button>Edit</button>}
176
- {canDelete && <button>Delete</button>}
177
- </div>
178
- );
179
- }
180
117
  ```
181
118
 
182
- ---
119
+ **That's it for frontend!** ✅
183
120
 
184
- ## API Reference
185
-
186
- ### Core Functions
121
+ ---
187
122
 
188
- #### `defineRoles(definitions)`
123
+ ## Backend Only
189
124
 
190
- Creates an immutable role registry.
125
+ ### Step 1: Define Roles
191
126
 
192
127
  ```typescript
193
- const registry = defineRoles({
194
- admin: ["user.read", "user.update"],
128
+ // lib/roles.ts
129
+ import { defineRoles } from "secure-role-guard/core";
130
+
131
+ export const roleRegistry = defineRoles({
132
+ admin: ["user.create", "user.read", "user.update", "user.delete"],
133
+ manager: ["user.read", "user.update"],
195
134
  viewer: ["user.read"],
196
135
  });
197
136
  ```
198
137
 
199
- #### `canUser(user, permission, registry)`
200
-
201
- Checks if a user has a specific permission. Returns `boolean`.
202
-
203
- ```typescript
204
- const allowed = canUser(user, "user.update", registry);
205
- ```
206
-
207
- #### `canUserAll(user, permissions, registry)`
208
-
209
- Checks if a user has ALL specified permissions.
210
-
211
- ```typescript
212
- const allowed = canUserAll(user, ["user.read", "user.update"], registry);
213
- ```
214
-
215
- #### `canUserAny(user, permissions, registry)`
216
-
217
- Checks if a user has ANY of the specified permissions.
218
-
219
- ```typescript
220
- const allowed = canUserAny(
221
- user,
222
- ["admin.access", "moderator.access"],
223
- registry
224
- );
225
- ```
226
-
227
- ### React Components
228
-
229
- #### `<PermissionProvider>`
230
-
231
- Provides permission context to child components.
232
-
233
- ```tsx
234
- <PermissionProvider user={user} registry={registry}>
235
- {children}
236
- </PermissionProvider>
237
- ```
238
-
239
- #### `<Can>`
240
-
241
- Conditionally renders children based on permissions.
242
-
243
- | Prop | Type | Description |
244
- | ------------- | ----------- | ------------------------------------- |
245
- | `permission` | `string` | Single permission to check |
246
- | `permissions` | `string[]` | Multiple permissions to check |
247
- | `anyOf` | `boolean` | If true, ANY permission grants access |
248
- | `fallback` | `ReactNode` | Content to show if denied |
249
- | `children` | `ReactNode` | Content to show if allowed |
250
-
251
- #### `<Cannot>`
252
-
253
- Inverse of `<Can>` - renders when permission is NOT granted.
254
-
255
- ### React Hooks
256
-
257
- | Hook | Returns | Description |
258
- | ------------------------ | ------------------------ | ----------------------- |
259
- | `useCan(permission)` | `boolean` | Check single permission |
260
- | `useCanAll(permissions)` | `boolean` | Check ALL permissions |
261
- | `useCanAny(permissions)` | `boolean` | Check ANY permission |
262
- | `usePermissions()` | `PermissionContextValue` | Full context access |
263
- | `useUser()` | `UserContext \| null` | Current user |
264
-
265
- ### User Context Shape
266
-
267
- ```typescript
268
- type UserContext = {
269
- userId?: string; // Optional user identifier
270
- roles?: string[]; // Array of role names
271
- permissions?: string[]; // Direct permissions (bypass roles)
272
- meta?: Record<string, unknown>; // Custom metadata (tenant, org, etc.)
273
- };
274
- ```
275
-
276
- ### Wildcard Permissions
277
-
278
- | Pattern | Grants Access To |
279
- | ---------------- | ------------------------------------------------ |
280
- | `*` | Everything |
281
- | `user.*` | `user.read`, `user.update`, `user.delete`, etc. |
282
- | `report.admin.*` | `report.admin.view`, `report.admin.export`, etc. |
283
-
284
- ---
285
-
286
- ## Real-World Examples
287
-
288
- ### Example 1: Next.js App with Express Backend
289
-
290
- **Frontend (Next.js App Router):**
291
-
292
- ```tsx
293
- // app/providers.tsx
294
- "use client";
295
-
296
- import { PermissionProvider } from "secure-role-guard/react";
297
- import { roleRegistry } from "@/lib/roles";
298
-
299
- export function Providers({
300
- children,
301
- user,
302
- }: {
303
- children: React.ReactNode;
304
- user: UserContext;
305
- }) {
306
- return (
307
- <PermissionProvider user={user} registry={roleRegistry}>
308
- {children}
309
- </PermissionProvider>
310
- );
311
- }
312
-
313
- // app/layout.tsx
314
- import { Providers } from "./providers";
315
- import { getUser } from "@/lib/auth"; // YOUR auth function
316
-
317
- export default async function RootLayout({ children }) {
318
- const user = await getUser(); // Fetch from session/JWT
319
-
320
- return (
321
- <html>
322
- <body>
323
- <Providers user={user}>{children}</Providers>
324
- </body>
325
- </html>
326
- );
327
- }
328
-
329
- // app/admin/page.tsx
330
- import { Can } from "secure-role-guard/react";
331
-
332
- export default function AdminPage() {
333
- return (
334
- <Can permission="admin.access" fallback={<p>Access Denied</p>}>
335
- <h1>Admin Dashboard</h1>
336
- </Can>
337
- );
338
- }
339
- ```
340
-
341
- **Backend (Express.js):**
138
+ ### Step 2: Use in Express
342
139
 
343
140
  ```typescript
344
141
  // server.ts
345
142
  import express from "express";
346
- import { defineRoles } from "secure-role-guard/core";
347
143
  import { requirePermission } from "secure-role-guard/adapters/express";
144
+ import { roleRegistry } from "./lib/roles";
348
145
 
349
146
  const app = express();
350
147
 
351
- // Define roles (same as frontend)
352
- const roleRegistry = defineRoles({
353
- admin: ["user.read", "user.update", "user.delete"],
354
- viewer: ["user.read"],
355
- });
356
-
357
- // YOUR auth middleware (not from this package)
358
- app.use(authMiddleware); // Sets req.user
148
+ // Your auth middleware (sets req.user)
149
+ app.use(yourAuthMiddleware);
359
150
 
360
151
  // Protected routes
361
152
  app.get(
@@ -366,11 +157,11 @@ app.get(
366
157
  }
367
158
  );
368
159
 
369
- app.put(
370
- "/api/users/:id",
371
- requirePermission("user.update", roleRegistry),
160
+ app.post(
161
+ "/api/users",
162
+ requirePermission("user.create", roleRegistry),
372
163
  (req, res) => {
373
- res.json({ success: true });
164
+ res.json({ created: true });
374
165
  }
375
166
  );
376
167
 
@@ -381,433 +172,157 @@ app.delete(
381
172
  res.json({ deleted: true });
382
173
  }
383
174
  );
384
-
385
- app.listen(3000);
386
175
  ```
387
176
 
388
- ---
389
-
390
- ### Example 2: React-Only App (Vite/CRA)
391
-
392
- ```tsx
393
- // src/roles.ts
394
- import { defineRoles } from "secure-role-guard";
395
-
396
- export const roleRegistry = defineRoles({
397
- admin: ["*"],
398
- editor: ["post.read", "post.create", "post.update"],
399
- viewer: ["post.read"],
400
- });
401
-
402
- // src/App.tsx
403
- import { PermissionProvider } from "secure-role-guard";
404
- import { roleRegistry } from "./roles";
405
- import { useAuth } from "./auth"; // YOUR auth hook
406
-
407
- function App() {
408
- const { user, isLoading } = useAuth();
409
-
410
- if (isLoading) return <div>Loading...</div>;
411
-
412
- return (
413
- <PermissionProvider user={user} registry={roleRegistry}>
414
- <Router>
415
- <Routes>
416
- <Route path="/" element={<Home />} />
417
- <Route path="/posts" element={<PostList />} />
418
- <Route path="/admin" element={<AdminRoute />} />
419
- </Routes>
420
- </Router>
421
- </PermissionProvider>
422
- );
423
- }
424
-
425
- // src/components/AdminRoute.tsx
426
- import { useCan } from "secure-role-guard";
427
- import { Navigate } from "react-router-dom";
428
-
429
- function AdminRoute() {
430
- const canAccess = useCan("admin.access");
431
-
432
- if (!canAccess) {
433
- return <Navigate to="/" replace />;
434
- }
435
-
436
- return <AdminPanel />;
437
- }
438
-
439
- // src/components/PostActions.tsx
440
- import { Can, Cannot } from "secure-role-guard";
441
-
442
- function PostActions({ postId }: { postId: string }) {
443
- return (
444
- <div>
445
- <Can permission="post.update">
446
- <button onClick={() => editPost(postId)}>Edit</button>
447
- </Can>
448
-
449
- <Can permission="post.delete">
450
- <button onClick={() => deletePost(postId)}>Delete</button>
451
- </Can>
452
-
453
- <Cannot permission="post.update">
454
- <span>View Only</span>
455
- </Cannot>
456
- </div>
457
- );
458
- }
459
- ```
460
-
461
- ---
462
-
463
- ### Example 3: Astro with React
177
+ ### Manual Check (Without Middleware)
464
178
 
465
179
  ```typescript
466
- // src/lib/roles.ts
467
- import { defineRoles } from 'secure-role-guard';
180
+ import { canUser } from "secure-role-guard/core";
468
181
 
469
- export const roleRegistry = defineRoles({
470
- admin: ['page.edit', 'page.publish', 'settings.manage'],
471
- editor: ['page.edit'],
472
- viewer: [],
182
+ app.put("/api/users/:id", (req, res) => {
183
+ if (!canUser(req.user, "user.update", roleRegistry)) {
184
+ return res.status(403).json({ error: "Forbidden" });
185
+ }
186
+ // ... your logic
473
187
  });
188
+ ```
474
189
 
475
- // src/components/AdminPanel.tsx (React component)
476
- import { PermissionProvider, Can, useCan } from 'secure-role-guard';
477
- import { roleRegistry } from '../lib/roles';
478
-
479
- interface Props {
480
- user: { roles: string[] } | null;
481
- }
482
-
483
- export default function AdminPanel({ user }: Props) {
484
- return (
485
- <PermissionProvider user={user} registry={roleRegistry}>
486
- <div className="admin-panel">
487
- <Can permission="page.edit">
488
- <PageEditor />
489
- </Can>
490
-
491
- <Can permission="settings.manage">
492
- <SettingsPanel />
493
- </Can>
494
-
495
- <Can permission="page.publish" fallback={<p>Publishing not available</p>}>
496
- <PublishButton />
497
- </Can>
498
- </div>
499
- </PermissionProvider>
500
- );
501
- }
502
-
503
- // src/pages/admin.astro
504
- ---
505
- import AdminPanel from '../components/AdminPanel';
506
- import { getUser } from '../lib/auth';
190
+ **That's it for backend!** ✅
507
191
 
508
- const user = await getUser(Astro.request);
509
192
  ---
510
193
 
511
- <AdminPanel client:load user={user} />
512
- ```
513
-
514
- ---
194
+ ## Full Stack
515
195
 
516
- ### Example 4: Next.js API Routes (App Router)
196
+ Use **same role definitions** for both:
517
197
 
518
198
  ```typescript
519
- // app/api/users/route.ts
520
- import { NextRequest, NextResponse } from "next/server";
521
- import { defineRoles, canUser } from "secure-role-guard/core";
522
- import { withPermission } from "secure-role-guard/adapters/nextjs";
523
- import { getUser } from "@/lib/auth";
524
-
525
- const roleRegistry = defineRoles({
526
- admin: ["user.read", "user.update", "user.delete"],
527
- viewer: ["user.read"],
528
- });
529
-
530
- // Option 1: Manual check
531
- export async function GET(request: NextRequest) {
532
- const user = await getUser(request);
533
-
534
- if (!canUser(user, "user.read", roleRegistry)) {
535
- return NextResponse.json({ error: "Forbidden" }, { status: 403 });
536
- }
537
-
538
- const users = await fetchUsers();
539
- return NextResponse.json(users);
540
- }
541
-
542
- // Option 2: Using wrapper
543
- export const PUT = withPermission(
544
- "user.update",
545
- roleRegistry,
546
- { getUser: async (req) => getUser(req) },
547
- async (request, user) => {
548
- const body = await request.json();
549
- const updated = await updateUser(body);
550
- return NextResponse.json(updated);
551
- }
552
- );
553
- ```
554
-
555
- ---
556
-
557
- ### Example 5: Multi-Tenant SaaS
199
+ // shared/roles.ts (shared between frontend & backend)
200
+ import { defineRoles } from "secure-role-guard";
558
201
 
559
- ```typescript
560
- import { defineRoles, canUser } from "secure-role-guard";
561
-
562
- // Define roles for your multi-tenant application
563
- const roleRegistry = defineRoles({
564
- org_owner: ["*"],
565
- org_admin: ["user.*", "billing.view", "settings.update"],
566
- org_member: ["user.read", "project.*"],
567
- org_viewer: ["user.read", "project.read"],
202
+ export const roleRegistry = defineRoles({
203
+ admin: ["*"], // Full access
204
+ manager: ["user.read", "user.update", "report.*"],
205
+ support: ["ticket.read", "ticket.reply"],
206
+ viewer: ["user.read"],
568
207
  });
569
-
570
- // User context with tenant metadata
571
- const currentUser = {
572
- userId: "usr_abc123",
573
- roles: ["org_admin"],
574
- permissions: ["beta.feature"], // Direct permission for beta access
575
- meta: {
576
- tenantId: "tenant_xyz",
577
- orgId: "org_456",
578
- plan: "enterprise",
579
- },
580
- };
581
-
582
- // Authorization check
583
- if (canUser(currentUser, "billing.view", roleRegistry)) {
584
- // Show billing dashboard
585
- }
586
-
587
- // Access tenant metadata for additional business logic
588
- const tenantId = currentUser.meta?.tenantId;
589
- if (tenantId) {
590
- // Filter data by tenant
591
- }
592
208
  ```
593
209
 
594
- ---
210
+ **Frontend:** Follow [Frontend Only](#frontend-only) steps
211
+ **Backend:** Follow [Backend Only](#backend-only) steps
595
212
 
596
- ## Framework Compatibility
597
-
598
- | Framework | Status | Import |
599
- | ---------------------- | ---------------- | ------------------------------------ |
600
- | Next.js (App Router) | ✅ Full support | `secure-role-guard` |
601
- | Next.js (Pages Router) | ✅ Full support | `secure-role-guard` |
602
- | Remix | ✅ Full support | `secure-role-guard` |
603
- | Gatsby | ✅ Full support | `secure-role-guard` |
604
- | Astro (React) | ✅ Full support | `secure-role-guard` |
605
- | Vite + React | ✅ Full support | `secure-role-guard` |
606
- | Create React App | ✅ Full support | `secure-role-guard` |
607
- | Express.js | ✅ Full support | `secure-role-guard/adapters/express` |
608
- | Fastify | 🔧 Adapter-ready | Use core directly |
609
- | Node HTTP | ✅ Full support | `secure-role-guard/core` |
213
+ > 💡 **Pro tip:** Keep roles in a shared package or copy to both projects.
610
214
 
611
215
  ---
612
216
 
613
- ## Common Mistakes to Avoid
217
+ ## 📚 API Reference
614
218
 
615
- ### DON'T: Parse JWT in this package
616
-
617
- ```typescript
618
- // WRONG - This package doesn't handle authentication
619
- import { canUser } from "secure-role-guard";
219
+ ### Core Functions
620
220
 
621
- const token = req.headers.authorization;
622
- const decoded = jwt.verify(token, secret); // NOT our job
623
- ```
221
+ | Function | Description |
222
+ | ----------------------------------------- | ----------------------- |
223
+ | `defineRoles(roles)` | Create role registry |
224
+ | `canUser(user, permission, registry)` | Check single permission |
225
+ | `canUserAll(user, permissions, registry)` | Check ALL permissions |
226
+ | `canUserAny(user, permissions, registry)` | Check ANY permission |
624
227
 
625
- ### DO: Pass already-authenticated user context
228
+ ### React Components
626
229
 
627
- ```typescript
628
- // CORRECT - You handle auth, we handle authorization
629
- import { canUser } from "secure-role-guard";
230
+ | Component | Description |
231
+ | --------------------------- | ------------------- |
232
+ | `<PermissionProvider>` | Wrap your app |
233
+ | `<Can permission="...">` | Show if allowed |
234
+ | `<Cannot permission="...">` | Show if NOT allowed |
630
235
 
631
- // Your auth middleware already verified and decoded the token
632
- const user = req.user; // Set by YOUR auth middleware
633
- const allowed = canUser(user, "admin.access", roleRegistry);
634
- ```
236
+ ### React Hooks
635
237
 
636
- ---
238
+ | Hook | Returns |
239
+ | ------------------------ | --------- |
240
+ | `useCan(permission)` | `boolean` |
241
+ | `useCanAll(permissions)` | `boolean` |
242
+ | `useCanAny(permissions)` | `boolean` |
637
243
 
638
- ### DON'T: Store user in global state
244
+ ### User Context Shape
639
245
 
640
246
  ```typescript
641
- // WRONG - Global state is a security smell
642
- let currentUser = null; // Anti-pattern
247
+ const user = {
248
+ userId: "user-123", // Optional
249
+ roles: ["admin", "manager"], // Role names
250
+ permissions: ["custom.perm"], // Direct permissions (bypass roles)
251
+ meta: { tenantId: "..." }, // Optional metadata
252
+ };
643
253
  ```
644
254
 
645
- ### DO: Pass user context explicitly
255
+ ### Wildcard Permissions
646
256
 
647
- ```typescript
648
- // CORRECT - Explicit is better than implicit
649
- <PermissionProvider user={user} registry={roleRegistry}>
650
- {children}
651
- </PermissionProvider>
652
- ```
257
+ | Pattern | Grants |
258
+ | ---------------- | ------------------------------------------------ |
259
+ | `*` | Everything |
260
+ | `user.*` | `user.read`, `user.update`, etc. |
261
+ | `report.admin.*` | `report.admin.view`, `report.admin.export`, etc. |
653
262
 
654
263
  ---
655
264
 
656
- ### DON'T: Use for authentication checks
265
+ ## 🔄 Dynamic Roles (From Database)
657
266
 
658
- ```typescript
659
- // WRONG - This is authentication, not authorization
660
- if (canUser(user, "logged-in", registry)) {
661
- // ...
662
- }
663
- ```
664
-
665
- ### ✅ DO: Check actual permissions
267
+ If admin creates roles at runtime:
666
268
 
667
269
  ```typescript
668
- // CORRECT - This is authorization
669
- if (canUser(user, "user.update", registry)) {
670
- // ...
671
- }
672
- ```
270
+ // Fetch roles from your database
271
+ const rolesFromDB = await fetchRolesFromDB();
673
272
 
674
- ---
675
-
676
- ### DON'T: Assume permissions exist
273
+ // Transform to: { roleName: ['permission1', 'permission2'] }
274
+ const roleDefinition = {};
275
+ rolesFromDB.forEach((role) => {
276
+ roleDefinition[role.name] = role.permissions;
277
+ });
677
278
 
678
- ```typescript
679
- // WRONG - May throw or behave unexpectedly
680
- if (user.permissions.includes("admin")) {
681
- // ...
682
- }
279
+ // Create registry
280
+ const registry = defineRoles(roleDefinition);
683
281
  ```
684
282
 
685
- ### DO: Use the provided functions
686
-
687
- ```typescript
688
- // CORRECT - Handles null/undefined safely (deny by default)
689
- if (canUser(user, "admin.access", registry)) {
690
- // ...
691
- }
692
- ```
283
+ Works with **any database**: MongoDB, PostgreSQL, MySQL, SQLite, etc.
693
284
 
694
285
  ---
695
286
 
696
- ## Backend Adapters
697
-
698
- ### Express Middleware
699
-
700
- ```typescript
701
- import {
702
- requirePermission,
703
- requireAllPermissions,
704
- requireAnyPermission,
705
- } from "secure-role-guard/adapters/express";
706
-
707
- // Single permission
708
- app.get("/api/users", requirePermission("user.read", registry), handler);
709
-
710
- // All permissions required
711
- app.delete(
712
- "/api/admin",
713
- requireAllPermissions(["admin.access", "data.delete"], registry),
714
- handler
715
- );
716
-
717
- // Any permission
718
- app.get(
719
- "/api/reports",
720
- requireAnyPermission(["report.view", "report.admin"], registry),
721
- handler
722
- );
723
-
724
- // Custom options
725
- app.put(
726
- "/api/settings",
727
- requirePermission("settings.update", registry, {
728
- statusCode: 401,
729
- message: "Unauthorized",
730
- getUser: (req) => req.session?.user,
731
- }),
732
- handler
733
- );
734
- ```
287
+ ## ⚠️ Important Notes
735
288
 
736
- ### Next.js Route Handlers
289
+ ### This Package Does NOT:
737
290
 
738
- ```typescript
739
- import {
740
- withPermission,
741
- checkNextPermission,
742
- } from "secure-role-guard/adapters/nextjs";
743
-
744
- // Using wrapper
745
- export const POST = withPermission(
746
- "post.create",
747
- registry,
748
- { getUser: async (req) => getUserFromSession(req) },
749
- async (request, user) => {
750
- return Response.json({ created: true });
751
- }
752
- );
291
+ - ❌ Handle authentication (JWT, sessions, cookies)
292
+ - ❌ Make API/database calls
293
+ - ❌ Store global state
753
294
 
754
- // Manual check
755
- export async function GET(request: NextRequest) {
756
- const user = await getUser(request);
757
- const result = checkNextPermission(user, "data.read", registry);
295
+ **You provide:** User with roles → **We check:** Permissions
758
296
 
759
- if (!result.allowed) {
760
- return Response.json({ error: "Forbidden" }, { status: 403 });
761
- }
297
+ ### Security
762
298
 
763
- return Response.json({ data: [] });
764
- }
765
- ```
299
+ - ✅ Deny by default (undefined = false)
300
+ - ✅ Zero dependencies in core
301
+ - ✅ Immutable configurations
302
+ - ✅ Pure functions (no side effects)
766
303
 
767
304
  ---
768
305
 
769
- ## TypeScript Support
306
+ ## 🔒 Backward Compatibility
770
307
 
771
- This package is written in TypeScript with strict mode enabled:
308
+ | Version | Meaning |
309
+ | ------------- | ------------------------------------------ |
310
+ | 1.0.x → 1.0.y | Bug fixes, safe to update |
311
+ | 1.x.0 → 1.y.0 | New features, no breaking changes |
312
+ | 1.x.x → 2.0.0 | Breaking changes, migration guide provided |
772
313
 
773
- ```typescript
774
- // tsconfig.json (package configuration)
775
- {
776
- "compilerOptions": {
777
- "strict": true,
778
- "noImplicitAny": true,
779
- "strictNullChecks": true,
780
- "exactOptionalPropertyTypes": true
781
- }
782
- }
783
- ```
784
-
785
- All types are exported:
786
-
787
- ```typescript
788
- import type {
789
- UserContext,
790
- RoleDefinition,
791
- RoleRegistry,
792
- PermissionCheckResult,
793
- } from "secure-role-guard";
794
- ```
314
+ **Promise:** v1.x APIs will never break. Update with confidence.
795
315
 
796
316
  ---
797
317
 
798
- ## License
318
+ ## 📄 License
799
319
 
800
320
  MIT © [Sohel Rahaman](https://github.com/sohelrahaman)
801
321
 
802
322
  ---
803
323
 
804
- ## Security Note
805
-
806
- This package is designed to be **boring, predictable, and auditable**. It intentionally avoids:
807
-
808
- - Magic behavior
809
- - Clever hacks
810
- - Hidden side effects
811
- - Runtime code generation
324
+ ## 🔗 Links
812
325
 
813
- If you find a security issue, please report it via [GitHub Issues](https://github.com/sohelrahaman/secure-role-guard/issues).
326
+ - [GitHub Repository](https://github.com/Sohel-Rahaman-Developer/secure-role-guard)
327
+ - [NPM Package](https://www.npmjs.com/package/secure-role-guard)
328
+ - [Report Issues](https://github.com/Sohel-Rahaman-Developer/secure-role-guard/issues)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secure-role-guard",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Zero-vulnerability, framework-agnostic RBAC authorization library for React applications",
5
5
  "author": "Sohel Rahaman",
6
6
  "license": "MIT",