jwt-middleware-auth 1.1.0 → 2.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.
Files changed (4) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/README.md +527 -0
  3. package/index.js +346 -44
  4. package/package.json +6 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,147 @@
1
+ # Changelog - JWT Middleware Authentication
2
+
3
+ ### Added
4
+
5
+ #### User & Admin Access Control
6
+ - **`verifyUserOwnershipOrAdmin`** - Middleware for user-specific resource access control
7
+ - Validates that authenticated user can only access/modify their own data
8
+ - Grants automatic access to admin and SuperManager roles
9
+ - Extracts user ID from route params (`:id` or `:userId`)
10
+ - Returns 401 if user not authenticated
11
+ - Returns 403 if user attempts to access another user's data
12
+ - Example: `router.put('/user/:id', verifyToken(secret), verifyUserOwnershipOrAdmin, controller)`
13
+ - Use cases: User profile updates, user data retrieval, user deletion
14
+
15
+ - **`verifyAdminOrSuperManager`** - Middleware for admin-only operations
16
+ - Restricts access to users with admin or SuperManager roles
17
+ - Returns 401 if user not authenticated
18
+ - Returns 403 if user lacks admin privileges
19
+ - Example: `router.get('/users', verifyToken(secret), verifyAdminOrSuperManager, controller)`
20
+ - Use cases: Admin dashboards, user management, sensitive data endpoints
21
+
22
+ #### Seller & Multi-Role Verification
23
+ - **`verifySellerOrAdmin(secret)`** - Middleware for seller/manager/admin/SuperManager role verification
24
+ - Validates that user has one of the elevated privilege roles
25
+ - Supports: admin, seller, manager, SuperManager
26
+ - Returns 403 if user lacks required privileges
27
+ - Example: `router.get('/store/:id/orders', verifySellerOrAdmin(secret), controller)`
28
+
29
+ - **`verifySellerRole(secret)`** - Middleware for strict seller role verification
30
+ - Validates that the authenticated user has seller or paymentManager role
31
+ - Focused on role validation for creating resources (more specific than verifySeller)
32
+ - Returns 403 if user does not have required role
33
+ - Example: `router.post('/offers', verifySellerRole(secret), controller)`
34
+
35
+ #### Advanced Ownership Verification
36
+ - **`verifyTokenAndOwnership(secret, getResourceOwnerId)`** - Higher-order middleware factory for resource ownership verification
37
+ - Verifies JWT token AND checks that the authenticated user owns the resource
38
+ - Accepts async function to retrieve resource owner ID
39
+ - Handles resource not found errors with 404 status
40
+ - Compares owner ID with authenticated user ID (handles both ObjectId and string types)
41
+ - Returns 403 if user is not the resource owner
42
+ - Returns 404 if resource not found
43
+ - Returns 500 for ownership verification errors
44
+ - Example usage:
45
+ ```javascript
46
+ const checkOfferOwnership = async (req) => {
47
+ const offer = await Offer.findById(req.params.id);
48
+ if (!offer) throw new Error('Offer not found');
49
+ return offer.sellerId;
50
+ };
51
+ router.put('/offer/:id', verifyTokenAndOwnership(secret, checkOfferOwnership), controller)
52
+ ```
53
+
54
+ - **`verifyTokenAndMultiOwnership(secret, getResourceOwnerIds)`** - Advanced multi-ownership verification
55
+ - Higher-order middleware factory for resources with multiple potential owners
56
+ - Automatically grants access to admin and SuperManager roles
57
+ - Accepts async function that returns array of owner IDs
58
+ - Checks if authenticated user matches any owner ID
59
+ - Useful for complex resources like orders (client OR seller can access)
60
+ - Returns 403 if user is not an owner or admin
61
+ - Returns 404 if resource not found
62
+ - Example usage:
63
+ ```javascript
64
+ const checkOrderOwnership = async (req) => {
65
+ const order = await Order.findById(req.params.id);
66
+ if (!order) throw new Error('Order not found');
67
+ const store = await Store.findById(order.storeId);
68
+ return [order.clientId, store.sellerId]; // Both can access
69
+ };
70
+ router.get('/order/:id', verifyTokenAndMultiOwnership(secret, checkOrderOwnership), controller)
71
+ ```
72
+
73
+ #### Role-Based Access Control (RBAC)
74
+ - **`authorizeRoles(...allowedRoles)`** - Middleware function for role-based access control
75
+ - Verifies authenticated users have one of the allowed roles
76
+ - Must be used after `verifyToken` middleware
77
+ - Supports multiple roles with flexible authorization logic
78
+ - Returns 401 if user is not authenticated
79
+ - Returns 403 if user lacks required permissions
80
+ - Example: `authorizeRoles('admin', 'manager')`
81
+
82
+ #### Core Middleware (Previously Included)
83
+ - **`verifyToken(secret)`** - Middleware to verify JWT tokens from request headers
84
+ - Expects token in 'token' header as 'Bearer <token>'
85
+ - Populates `req.user` with decoded token payload
86
+
87
+ - **`verifyTokenAndAuthorization(secret)`** - Middleware for user self-update authorization
88
+ - Allows access if user is updating their own account OR is an admin
89
+
90
+ - **`verifyAdmin(secret)`** - Middleware to verify admin privileges
91
+ - Checks `req.user.isAdmin` property
92
+
93
+ - **`verifyManager(secret)`** - Middleware to verify manager role or self-access
94
+ - Allows access if user is a manager OR accessing their own resource
95
+
96
+ - **`verifySeller(secret)`** - Middleware to verify seller/payment manager role
97
+ - Allows access if user is a seller, payment manager, OR accessing their own resource
98
+
99
+ ### Changed
100
+ - Updated package description to reflect role-based authorization capabilities
101
+ - Enhanced JSDoc documentation for all middleware functions with detailed examples
102
+ - Added comprehensive inline comments explaining middleware behavior
103
+ - Exported all new middleware functions in module exports
104
+ - Package keywords updated to include: "authorization", "roles", "rbac"
105
+
106
+ ### Improved
107
+ - Enhanced user data protection with ownership verification
108
+ - Centralized admin role verification for consistent access control
109
+ - Better separation of concerns between user-level and admin-level operations
110
+ - Enhanced support for multi-tenant authorization patterns
111
+ - Better handling of resources with multiple owners or access levels
112
+ - Consistent admin/SuperManager privilege handling across all ownership middlewares
113
+ - Enhanced flexibility for seller-related authorization with separate role-checking middleware
114
+ - Better separation of concerns between role verification and ownership verification
115
+ - Comprehensive error handling for resource ownership validation
116
+ - Better error handling in `authorizeRoles` middleware
117
+ - More descriptive error messages for authentication and authorization failures
118
+
119
+ ---
120
+
121
+ ## Summary
122
+
123
+ **Total Middleware Functions:** 13
124
+
125
+ **Categories:**
126
+ - Core Authentication: 1 (verifyToken)
127
+ - Admin & Role Verification: 4 (verifyAdmin, verifyAdminOrSuperManager, authorizeRoles, verifyManager)
128
+ - Seller Verification: 3 (verifySeller, verifySellerRole, verifySellerOrAdmin)
129
+ - Ownership & Authorization: 3 (verifyTokenAndAuthorization, verifyTokenAndOwnership, verifyTokenAndMultiOwnership)
130
+ - User Access Control: 1 (verifyUserOwnershipOrAdmin)
131
+
132
+ **Key Features:**
133
+ - Complete JWT token verification
134
+ - Flexible role-based access control (RBAC)
135
+ - Resource ownership verification (single & multi-owner)
136
+ - User self-access & admin privilege management
137
+ - Seller/Manager role validation
138
+ - Comprehensive error handling
139
+ - TypeScript-ready with JSDoc documentation
140
+
141
+ **Dependencies:**
142
+ - jsonwebtoken: ^9.0.3
143
+
144
+ **Migration Notes:**
145
+ - All changes are additive - existing code continues to work
146
+ - Custom authorization middleware can be replaced with centralized package functions
147
+ - Simply import new middleware functions alongside existing ones for enhanced capabilities
package/README.md ADDED
@@ -0,0 +1,527 @@
1
+ # JWT Middleware Auth
2
+
3
+ A comprehensive middleware library for JWT authentication and role-based authorization in Express.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install jwt-middleware-auth
9
+ ```
10
+
11
+ ## Dependencies
12
+
13
+ This package requires jsonwebtoken:
14
+
15
+ ```bash
16
+ npm install jsonwebtoken@^9.0.3
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Basic Setup
22
+
23
+ ```javascript
24
+ const express = require('express');
25
+ const { verifyToken, authorizeRoles } = require('jwt-middleware-auth');
26
+
27
+ const app = express();
28
+ const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
29
+
30
+ // Public route
31
+ app.get('/api/public', (req, res) => {
32
+ res.json({ message: 'Public data' });
33
+ });
34
+
35
+ // Protected route - requires authentication
36
+ app.get('/api/profile', verifyToken(JWT_SECRET), (req, res) => {
37
+ res.json({ user: req.user });
38
+ });
39
+
40
+ // Role-based route - requires specific role
41
+ app.get('/api/admin', verifyToken(JWT_SECRET), authorizeRoles('admin'), (req, res) => {
42
+ res.json({ message: 'Admin data' });
43
+ });
44
+ ```
45
+
46
+ ### Token Format
47
+
48
+ Tokens should be sent in the request header:
49
+
50
+ ```
51
+ token: Bearer <your-jwt-token>
52
+ ```
53
+
54
+ Example:
55
+ ```
56
+ token: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
57
+ ```
58
+
59
+ ## Available Middlewares
60
+
61
+ ### Authentication Middlewares
62
+
63
+ #### `verifyToken(secret)`
64
+
65
+ Verifies JWT token and populates `req.user` with decoded payload.
66
+
67
+ ```javascript
68
+ app.get('/api/protected', verifyToken(JWT_SECRET), (req, res) => {
69
+ // req.user contains: { id, role, email, ... }
70
+ res.json({ userId: req.user.id });
71
+ });
72
+ ```
73
+
74
+ **Response on failure:**
75
+ - `401` - Token not provided or invalid token
76
+
77
+ ---
78
+
79
+ ### Authorization Middlewares
80
+
81
+ #### `authorizeRoles(...allowedRoles)`
82
+
83
+ Checks if authenticated user has one of the allowed roles. Must be used after `verifyToken`.
84
+
85
+ ```javascript
86
+ // Single role
87
+ app.get('/api/admin-only',
88
+ verifyToken(JWT_SECRET),
89
+ authorizeRoles('admin'),
90
+ controller
91
+ );
92
+
93
+ // Multiple roles
94
+ app.get('/api/management',
95
+ verifyToken(JWT_SECRET),
96
+
97
+ ### Request Validation Middlewares
98
+
99
+ #### `validateDashboardLogin`
100
+
101
+ Validates the dashboard login request body.
102
+
103
+ **Rules:**
104
+ - `role` must NOT be provided
105
+ - `email` is required
106
+ - `password` is required unless `google` is truthy
107
+
108
+ ```javascript
109
+ const express = require('express');
110
+ const { validateDashboardLogin } = require('jwt-middleware-auth');
111
+
112
+ const router = express.Router();
113
+
114
+ router.post('/dashboard/login', validateDashboardLogin, loginController);
115
+ ```
116
+
117
+ **Failure behavior:**
118
+ - Returns `400` with a JSON `{ message }` describing the validation error.
119
+
120
+ authorizeRoles('admin', 'manager', 'SuperManager'),
121
+ controller
122
+ );
123
+ ```
124
+
125
+ **Response on failure:**
126
+ - `401` - User not authenticated
127
+ - `403` - User doesn't have required role
128
+
129
+ **Supported roles:**
130
+ - `user` - Regular user
131
+ - `admin` - Administrator
132
+ - `seller` - Store seller/vendor
133
+ - `paymentManager` - Payment processing manager
134
+ - `manager` - General manager
135
+ - `SuperManager` - Super administrator
136
+
137
+ ---
138
+
139
+ #### `verifyTokenAndAuthorization(secret)`
140
+
141
+ Allows access if user is updating their own account OR is an admin.
142
+
143
+ ```javascript
144
+ // User can update their own profile, or admin can update any profile
145
+ app.put('/api/users/:id',
146
+ verifyTokenAndAuthorization(JWT_SECRET),
147
+ updateUserController
148
+ );
149
+ ```
150
+
151
+ **Access granted when:**
152
+ - `req.user.id === req.params.id` (own account)
153
+ - `req.user.role === 'admin'` (admin override)
154
+
155
+ ---
156
+
157
+ #### `verifyAdmin(secret)`
158
+
159
+ Verifies user has admin privileges.
160
+
161
+ ```javascript
162
+ app.delete('/api/users/:id',
163
+ verifyAdmin(JWT_SECRET),
164
+ deleteUserController
165
+ );
166
+ ```
167
+
168
+ **Access granted when:**
169
+ - `req.user.isAdmin === true`
170
+
171
+ ---
172
+
173
+ #### `verifyManager(secret)`
174
+
175
+ Allows access if user is a manager or accessing their own resource.
176
+
177
+ ```javascript
178
+ app.get('/api/reports/:id',
179
+ verifyManager(JWT_SECRET),
180
+ getReportController
181
+ );
182
+ ```
183
+
184
+ **Access granted when:**
185
+ - `req.user.id == req.params.id` (own resource)
186
+ - `req.user.role == 'manager'` (manager access)
187
+
188
+ ---
189
+
190
+ #### `verifySeller(secret)`
191
+
192
+ Allows access if user is a seller, payment manager, or accessing their own resource.
193
+
194
+ ```javascript
195
+ app.get('/api/stores/:id',
196
+ verifySeller(JWT_SECRET),
197
+ getStoreController
198
+ );
199
+ ```
200
+
201
+ **Access granted when:**
202
+ - `req.user.id == req.params.id` (own resource)
203
+ - `req.user.role == 'seller'` (seller access)
204
+ - `req.user.role == 'paymentManager'` (payment manager access)
205
+
206
+ ---
207
+
208
+ #### `verifySellerRole(secret)`
209
+
210
+ Focused role validation for sellers - ideal for resource creation.
211
+
212
+ ```javascript
213
+ app.post('/api/offers',
214
+ verifySellerRole(JWT_SECRET),
215
+ createOfferController
216
+ );
217
+ ```
218
+
219
+ **Access granted when:**
220
+ - `req.user.role === 'seller'`
221
+ - `req.user.role === 'paymentManager'`
222
+
223
+ ---
224
+
225
+ #### `verifySellerOrAdmin(secret)`
226
+
227
+ Allows access for sellers, managers, admins, or super managers.
228
+
229
+ ```javascript
230
+ app.get('/api/store/:id/orders',
231
+ verifySellerOrAdmin(JWT_SECRET),
232
+ getOrdersController
233
+ );
234
+ ```
235
+
236
+ **Access granted when:**
237
+ - `req.user.role` is one of: `admin`, `seller`, `manager`, `SuperManager`
238
+
239
+ ---
240
+
241
+ ### Advanced Middlewares
242
+
243
+ #### `verifyTokenAndOwnership(secret, getResourceOwnerId)`
244
+
245
+ Higher-order middleware that verifies token and checks resource ownership.
246
+
247
+ ```javascript
248
+ const { verifyTokenAndOwnership } = require('jwt-middleware-auth');
249
+ const Offer = require('./models/Offer');
250
+
251
+ // Define ownership check function
252
+ const checkOfferOwnership = async (req) => {
253
+ const offer = await Offer.findById(req.params.id);
254
+ if (!offer) {
255
+ throw new Error('Offer not found');
256
+ }
257
+ return offer.sellerId; // Return the owner ID
258
+ };
259
+
260
+ // Apply to route
261
+ app.put('/api/offers/:id',
262
+ verifyTokenAndOwnership(JWT_SECRET, checkOfferOwnership),
263
+ updateOfferController
264
+ );
265
+ ```
266
+
267
+ **Parameters:**
268
+ - `secret` - JWT secret key
269
+ - `getResourceOwnerId` - Async function that returns the resource owner's ID
270
+
271
+ **Access granted when:**
272
+ - `ownerId === req.user.id` (user owns the resource)
273
+
274
+ **Responses:**
275
+ - `404` - Resource not found
276
+ - `403` - User doesn't own the resource
277
+ - `500` - Error during ownership verification
278
+
279
+ ---
280
+
281
+ ## Complete Examples
282
+
283
+ ### User Management API
284
+
285
+ ```javascript
286
+ const express = require('express');
287
+ const {
288
+ verifyToken,
289
+ verifyTokenAndAuthorization,
290
+ verifyAdmin
291
+ } = require('jwt-middleware-auth');
292
+
293
+ const app = express();
294
+ const JWT_SECRET = process.env.JWT_SECRET;
295
+
296
+ // Get own profile or any profile (admin)
297
+ app.get('/api/users/:id',
298
+ verifyTokenAndAuthorization(JWT_SECRET),
299
+ (req, res) => {
300
+ // Fetch and return user
301
+ }
302
+ );
303
+
304
+ // Update own profile or any profile (admin)
305
+ app.put('/api/users/:id',
306
+ verifyTokenAndAuthorization(JWT_SECRET),
307
+ (req, res) => {
308
+ // Update user
309
+ }
310
+ );
311
+
312
+ // Delete user (admin only)
313
+ app.delete('/api/users/:id',
314
+ verifyAdmin(JWT_SECRET),
315
+ (req, res) => {
316
+ // Delete user
317
+ }
318
+ );
319
+
320
+ // List all users (admin only)
321
+ app.get('/api/users',
322
+ verifyAdmin(JWT_SECRET),
323
+ (req, res) => {
324
+ // Return all users
325
+ }
326
+ );
327
+ ```
328
+
329
+ ### E-commerce Store API
330
+
331
+ ```javascript
332
+ const {
333
+ verifyToken,
334
+ authorizeRoles,
335
+ verifySellerRole,
336
+ verifyTokenAndOwnership
337
+ } = require('jwt-middleware-auth');
338
+
339
+ const Store = require('./models/Store');
340
+
341
+ // Get store ownership
342
+ const checkStoreOwnership = async (req) => {
343
+ const store = await Store.findById(req.params.id);
344
+ if (!store) throw new Error('Store not found');
345
+ return store.ownerId;
346
+ };
347
+
348
+ // Create store (seller only)
349
+ app.post('/api/stores',
350
+ verifySellerRole(JWT_SECRET),
351
+ createStoreController
352
+ );
353
+
354
+ // Update own store
355
+ app.put('/api/stores/:id',
356
+ verifyTokenAndOwnership(JWT_SECRET, checkStoreOwnership),
357
+ updateStoreController
358
+ );
359
+
360
+ // View store analytics (seller, manager, admin)
361
+ app.get('/api/stores/:id/analytics',
362
+ verifyToken(JWT_SECRET),
363
+ authorizeRoles('seller', 'manager', 'admin'),
364
+ getAnalyticsController
365
+ );
366
+
367
+ // Approve store (admin only)
368
+ app.post('/api/stores/:id/approve',
369
+ verifyToken(JWT_SECRET),
370
+ authorizeRoles('admin'),
371
+ approveStoreController
372
+ );
373
+ ```
374
+
375
+ ### Multi-Role Management System
376
+
377
+ ```javascript
378
+ const { verifyToken, authorizeRoles } = require('jwt-middleware-auth');
379
+
380
+ // Dashboard access - multiple roles
381
+ app.get('/api/dashboard',
382
+ verifyToken(JWT_SECRET),
383
+ authorizeRoles('admin', 'manager', 'SuperManager'),
384
+ (req, res) => {
385
+ res.json({ dashboard: 'data' });
386
+ }
387
+ );
388
+
389
+ // Financial reports - restricted roles
390
+ app.get('/api/reports/financial',
391
+ verifyToken(JWT_SECRET),
392
+ authorizeRoles('admin', 'paymentManager', 'SuperManager'),
393
+ (req, res) => {
394
+ res.json({ reports: 'financial data' });
395
+ }
396
+ );
397
+
398
+ // System settings - super admin only
399
+ app.put('/api/settings',
400
+ verifyToken(JWT_SECRET),
401
+ authorizeRoles('SuperManager'),
402
+ (req, res) => {
403
+ res.json({ message: 'Settings updated' });
404
+ }
405
+ );
406
+ ```
407
+
408
+ ## Error Responses
409
+
410
+ All middlewares return consistent error responses:
411
+
412
+ ### 401 Unauthorized
413
+
414
+ ```json
415
+ {
416
+ "message": "Token is not provided"
417
+ }
418
+ ```
419
+
420
+ ```json
421
+ {
422
+ "message": "Invalid Token"
423
+ }
424
+ ```
425
+
426
+ ```json
427
+ {
428
+ "message": "Authentication required"
429
+ }
430
+ ```
431
+
432
+ ### 403 Forbidden
433
+
434
+ ```json
435
+ {
436
+ "message": "You are not authorized"
437
+ }
438
+ ```
439
+
440
+ ```json
441
+ {
442
+ "message": "Access denied. Insufficient permissions."
443
+ }
444
+ ```
445
+
446
+ ```json
447
+ {
448
+ "message": "Seller role required"
449
+ }
450
+ ```
451
+
452
+ ```json
453
+ {
454
+ "message": "You are not authorized to access this resource"
455
+ }
456
+ ```
457
+
458
+ ### 404 Not Found
459
+
460
+ ```json
461
+ {
462
+ "message": "Resource not found"
463
+ }
464
+ ```
465
+
466
+ ### 500 Internal Server Error
467
+
468
+ ```json
469
+ {
470
+ "message": "Error verifying resource ownership"
471
+ }
472
+ ```
473
+
474
+ ## JWT Payload Structure
475
+
476
+ Your JWT tokens should include:
477
+
478
+ ```javascript
479
+ {
480
+ id: 'user-id', // Required for ownership checks
481
+ role: 'user', // Required for role-based access
482
+ isAdmin: false, // Optional for admin checks
483
+ email: 'user@example.com', // Optional
484
+ // ... other claims
485
+ }
486
+ ```
487
+
488
+ ## Best Practices
489
+
490
+ 1. **Use environment variables for secrets** - Never hardcode JWT secrets
491
+ 2. **Chain middlewares properly** - Always use `verifyToken` before authorization middlewares
492
+ 3. **Choose appropriate middleware** - Use specific middlewares (e.g., `authorizeRoles`) over general ones
493
+ 4. **Handle errors gracefully** - Use error middleware to catch and format authentication errors
494
+ 5. **Implement token refresh** - Consider implementing refresh token mechanism for better security
495
+ 6. **Short-lived tokens** - Use short expiration times for access tokens
496
+ 7. **Validate token payload** - Ensure JWT payload contains required fields (id, role)
497
+
498
+ ## Security Considerations
499
+
500
+ - Store JWT secrets securely (environment variables, secret managers)
501
+ - Use HTTPS in production to prevent token interception
502
+ - Implement token expiration and refresh mechanisms
503
+ - Consider token revocation strategy for logout
504
+ - Validate and sanitize all user inputs
505
+ - Use strong, random secret keys (minimum 32 characters)
506
+ - Implement rate limiting on authentication endpoints
507
+ - Log authentication failures for security monitoring
508
+
509
+ ## Middleware Comparison
510
+
511
+ | Middleware | Authentication | Authorization | Use Case |
512
+ |------------|----------------|---------------|----------|
513
+ | `verifyToken` | ✅ | ❌ | Basic authentication |
514
+ | `authorizeRoles` | ❌* | ✅ | Role-based access |
515
+ | `verifyTokenAndAuthorization` | ✅ | ✅ | Self or admin access |
516
+ | `verifyAdmin` | ✅ | ✅ | Admin-only access |
517
+ | `verifyManager` | ✅ | ✅ | Manager or self access |
518
+ | `verifySeller` | ✅ | ✅ | Seller/payment manager or self |
519
+ | `verifySellerRole` | ✅ | ✅ | Seller role validation |
520
+ | `verifySellerOrAdmin` | ✅ | ✅ | Elevated privileges |
521
+ | `verifyTokenAndOwnership` | ✅ | ✅ | Resource ownership |
522
+
523
+ *Requires `verifyToken` to be called first
524
+
525
+ ## License
526
+
527
+ MIT
package/index.js CHANGED
@@ -1,24 +1,66 @@
1
1
  const jwt = require('jsonwebtoken');
2
2
 
3
+ /**
4
+ * Middleware to verify JWT token from request headers
5
+ * Expects token in 'token' header as 'Bearer <token>'
6
+ * Populates req.user with decoded token payload
7
+ * @param {string} secret - JWT secret for token verification
8
+ * @returns {Function} Express middleware function
9
+ */
3
10
  const verifyToken = (secret) => {
4
- return (req, res, next) => {
5
- const authHeader = req.headers.token;
6
-
7
- if (authHeader) {
8
- const token = authHeader.split(' ')[1];
9
- jwt.verify(token, secret, (err, user) => {
10
- if (err) {
11
- return res.status(401).json({ message: 'Invalid Token' });
12
- }
13
- req.user = user;
14
- next();
15
- });
16
- } else {
17
- return res.status(401).json({ message: 'Token is not provided' });
11
+ return (req, res, next) => {
12
+ const authHeader = req.headers.token;
13
+
14
+ if (authHeader) {
15
+ const token = authHeader.split(' ')[1];
16
+ jwt.verify(token, secret, (err, user) => {
17
+ if (err) {
18
+ return res.status(401).json({ message: 'Invalid Token' });
19
+ }
20
+ req.user = user;
21
+ next();
22
+ });
23
+ } else {
24
+ return res.status(401).json({ message: 'Token is not provided' });
25
+ }
26
+ };
27
+ };
28
+
29
+ /**
30
+ * Middleware to check if the authenticated user has one of the allowed roles
31
+ * Must be used after verifyToken middleware as it expects req.user to be populated
32
+ * @param {...string} allowedRoles - Roles that are allowed to access the route
33
+ * @returns {Function} Express middleware function
34
+ * @example
35
+ * router.get('/admin-only', verifyToken(secret), authorizeRoles('admin'), controller)
36
+ * router.get('/multi-role', verifyToken(secret), authorizeRoles('admin', 'manager'), controller)
37
+ */
38
+ const authorizeRoles = (...allowedRoles) => {
39
+ return (req, res, next) => {
40
+ try {
41
+ // Check if user is authenticated (should be set by verifyToken middleware)
42
+ if (!req.user) {
43
+ return res.status(401).json({ message: 'Authentication required' });
44
+ }
45
+
46
+ // Check if user has one of the allowed roles
47
+ if (!allowedRoles.includes(req.user.role)) {
48
+ return res.status(403).json({ message: 'Access denied. Insufficient permissions.' });
18
49
  }
19
- };
50
+
51
+ next();
52
+ } catch (err) {
53
+ next(err);
54
+ }
20
55
  };
21
- //for updatin User information
56
+ };
57
+
58
+ /**
59
+ * Middleware for updating user information
60
+ * Allows access if user is updating their own account OR is an admin
61
+ * @param {string} secret - JWT secret for token verification
62
+ * @returns {Function} Express middleware function
63
+ */
22
64
  const verifyTokenAndAuthorization = (secret) => {
23
65
  return (req, res, next) => {
24
66
  verifyToken(secret)(req, res, () => {
@@ -30,39 +72,299 @@ const verifyTokenAndAuthorization = (secret) => {
30
72
  };
31
73
  };
32
74
 
33
- // for all access};
75
+ /**
76
+ * Middleware to verify if user is an admin
77
+ * @param {string} secret - JWT secret for token verification
78
+ * @returns {Function} Express middleware function
79
+ */
34
80
  const verifyAdmin = (secret) => {
35
- return (req, res, next) => {
36
- verifyToken(secret)(req, res, () => {
37
- if (req.user.isAdmin || req.user.isAdmin == true) {
38
- return next();
39
- }
40
- return res.status(403).json({ message: 'You are not an admin' });
41
- });
42
- };
81
+ return (req, res, next) => {
82
+ verifyToken(secret)(req, res, () => {
83
+ if (req.user.isAdmin || req.user.isAdmin == true) {
84
+ return next();
85
+ }
86
+ return res.status(403).json({ message: 'You are not an admin' });
87
+ });
43
88
  };
89
+ };
44
90
 
45
- // for all access
91
+ /**
92
+ * Middleware to verify if user is a manager or accessing their own resource
93
+ * @param {string} secret - JWT secret for token verification
94
+ * @returns {Function} Express middleware function
95
+ */
46
96
  const verifyManager = (secret) => {
47
- return (req, res, next) => {
48
- verifyToken(secret)(req, res, () => {
49
- if (req.user.id == req.params.id || req.user.role == 'manager') {
50
- return next();
51
- }
52
- return res.status(403).json({ message: 'You are not a manager' });
53
- });
54
- };
97
+ return (req, res, next) => {
98
+ verifyToken(secret)(req, res, () => {
99
+ if (req.user.id == req.params.id || req.user.role == 'manager') {
100
+ return next();
101
+ }
102
+ return res.status(403).json({ message: 'You are not a manager' });
103
+ });
55
104
  };
56
- //for seller access
105
+ };
106
+
107
+ /**
108
+ * Middleware to verify if user is a seller, payment manager, or accessing their own resource
109
+ * @param {string} secret - JWT secret for token verification
110
+ * @returns {Function} Express middleware function
111
+ */
57
112
  const verifySeller = (secret) => {
58
- return (req, res, next) => {
59
- verifyToken(secret)(req, res, () => {
60
- if (req.user.id == req.params.id || req.user.role == 'seller' || req.user.role == 'paymentManager') {
61
- return next();
62
- }
63
- return res.status(403).json({ message: 'You are not authorized' });
64
- });
65
- };
113
+ return (req, res, next) => {
114
+ verifyToken(secret)(req, res, () => {
115
+ if (req.user.id == req.params.id || req.user.role == 'seller' || req.user.role == 'paymentManager') {
116
+ return next();
117
+ }
118
+ return res.status(403).json({ message: 'You are not authorized' });
119
+ });
120
+ };
121
+ };
122
+
123
+ /**
124
+ * Middleware to verify seller role and optionally check resource ownership
125
+ * More flexible than verifySeller - focuses on role validation for creating resources
126
+ * @param {string} secret - JWT secret for token verification
127
+ * @returns {Function} Express middleware function
128
+ * @example
129
+ * router.post('/offers', verifySellerRole(secret), controller)
130
+ */
131
+ const verifySellerRole = (secret) => {
132
+ return (req, res, next) => {
133
+ verifyToken(secret)(req, res, () => {
134
+ if (req.user.role === 'seller' || req.user.role === 'paymentManager') {
135
+ return next();
136
+ }
137
+ return res.status(403).json({ message: 'Seller role required' });
138
+ });
66
139
  };
140
+ };
141
+
142
+ /**
143
+ * Higher-order middleware factory to verify token and check resource ownership
144
+ * Validates that the authenticated user owns the resource before allowing access
145
+ * @param {string} secret - JWT secret for token verification
146
+ * @param {Function} getResourceOwnerId - Async function that retrieves the owner ID of the resource
147
+ * Function receives (req) and should return the owner ID or throw error if resource not found
148
+ * @returns {Function} Express middleware function
149
+ * @example
150
+ * // For offer ownership check
151
+ * const checkOfferOwnership = async (req) => {
152
+ * const offer = await Offer.findById(req.params.id);
153
+ * if (!offer) throw new Error('Offer not found');
154
+ * return offer.sellerId;
155
+ * };
156
+ * router.put('/offer/:id', verifyTokenAndOwnership(secret, checkOfferOwnership), controller)
157
+ */
158
+ const verifyTokenAndOwnership = (secret, getResourceOwnerId) => {
159
+ return (req, res, next) => {
160
+ verifyToken(secret)(req, res, async () => {
161
+ try {
162
+ const ownerId = await getResourceOwnerId(req);
163
+
164
+ // Convert both to strings for comparison to handle ObjectId and string types
165
+ if (ownerId.toString() === req.user.id.toString()) {
166
+ return next();
167
+ }
168
+
169
+ return res.status(403).json({ message: 'You are not authorized to access this resource' });
170
+ } catch (error) {
171
+ // If resource not found or other errors during ownership check
172
+ if (error.message.includes('not found')) {
173
+ return res.status(404).json({ message: error.message });
174
+ }
175
+ return res.status(500).json({ message: 'Error verifying resource ownership' });
176
+ }
177
+ });
178
+ };
179
+ };
180
+
181
+ /**
182
+ * Middleware to verify seller, manager, admin, or SuperManager roles
183
+ * Used for operations that require elevated privileges
184
+ * @param {string} secret - JWT secret for token verification
185
+ * @returns {Function} Express middleware function
186
+ * @example
187
+ * router.get('/store/:id/orders', verifySellerOrAdmin(secret), controller)
188
+ */
189
+ const verifySellerOrAdmin = (secret) => {
190
+ return (req, res, next) => {
191
+ verifyToken(secret)(req, res, () => {
192
+ const allowedRoles = ['admin', 'seller', 'manager', 'SuperManager'];
193
+ if (allowedRoles.includes(req.user.role)) {
194
+ return next();
195
+ }
196
+ return res.status(403).json({ message: 'Access denied. Seller or admin privileges required' });
197
+ });
198
+ };
199
+ };
200
+
201
+ /**
202
+ * Higher-order middleware factory for complex ownership verification
203
+ * Allows access if user is admin/SuperManager OR matches one of the owner IDs
204
+ * Useful for resources that can have multiple owners (e.g., orders - client or seller)
205
+ * @param {string} secret - JWT secret for token verification
206
+ * @param {Function} getResourceOwnerIds - Async function that retrieves owner IDs
207
+ * Function receives (req) and should return an array of owner IDs or throw error
208
+ * @returns {Function} Express middleware function
209
+ * @example
210
+ * // For order ownership check (client or seller of the store)
211
+ * const checkOrderOwnership = async (req) => {
212
+ * const order = await Order.findById(req.params.id);
213
+ * if (!order) throw new Error('Order not found');
214
+ * const store = await Store.findById(order.storeId);
215
+ * return [order.clientId, store.sellerId]; // Both can access
216
+ * };
217
+ * router.get('/order/:id', verifyTokenAndMultiOwnership(secret, checkOrderOwnership), controller)
218
+ */
219
+ const verifyTokenAndMultiOwnership = (secret, getResourceOwnerIds) => {
220
+ return (req, res, next) => {
221
+ verifyToken(secret)(req, res, async () => {
222
+ try {
223
+ // Admin and SuperManager can access all resources
224
+ if (req.user.role === 'admin' || req.user.role === 'SuperManager') {
225
+ return next();
226
+ }
227
+
228
+ const ownerIds = await getResourceOwnerIds(req);
229
+ const userId = req.user.id.toString();
230
+
231
+ // Check if user ID matches any of the owner IDs
232
+ const hasAccess = ownerIds.some(ownerId => ownerId.toString() === userId);
233
+
234
+ if (hasAccess) {
235
+ return next();
236
+ }
237
+
238
+ return res.status(403).json({ message: 'Access denied to this resource' });
239
+ } catch (error) {
240
+ if (error.message.includes('not found')) {
241
+ return res.status(404).json({ message: error.message });
242
+ }
243
+ return res.status(500).json({ message: 'Error verifying resource ownership' });
244
+ }
245
+ });
246
+ };
247
+ };
248
+
249
+ /**
250
+ * Middleware to verify that the authenticated user can only access/modify their own data
251
+ * or is an admin who can access any data
252
+ * Designed for user-specific endpoints where ID is in route params
253
+ * @param {Object} req - Express request object
254
+ * @param {Object} res - Express response object
255
+ * @param {Function} next - Express next middleware function
256
+ * @returns {Function} Express middleware function
257
+ * @example
258
+ * router.put('/user/:id', verifyToken(secret), verifyUserOwnershipOrAdmin, controller)
259
+ * router.get('/user/find/:id', verifyToken(secret), verifyUserOwnershipOrAdmin, controller)
260
+ */
261
+ const verifyUserOwnershipOrAdmin = (req, res, next) => {
262
+ try {
263
+ // req.user is set by verifyToken middleware
264
+ if (!req.user) {
265
+ return res.status(401).json({ message: 'Authentication required' });
266
+ }
267
+
268
+ const authenticatedUserId = req.user.id;
269
+ const requestedUserId = req.params.id || req.params.userId;
270
+ const userRole = req.user.role;
271
+
272
+ // Allow if user is accessing their own data or is an admin
273
+ if (authenticatedUserId === requestedUserId || userRole === 'admin' || userRole === 'SuperManager') {
274
+ return next();
275
+ }
276
+
277
+ return res.status(403).json({ message: 'Access denied. You can only access your own data' });
278
+ } catch (err) {
279
+ next(err);
280
+ }
281
+ };
282
+
283
+ /**
284
+ * Middleware to verify admin or SuperManager role
285
+ * Restricts access to admin-only operations
286
+ * @param {Object} req - Express request object
287
+ * @param {Object} res - Express response object
288
+ * @param {Function} next - Express next middleware function
289
+ * @returns {Function} Express middleware function
290
+ * @example
291
+ * router.get('/users', verifyToken(secret), verifyAdminOrSuperManager, controller)
292
+ * router.get('/sellers', verifyToken(secret), verifyAdminOrSuperManager, controller)
293
+ */
294
+ const verifyAdminOrSuperManager = (req, res, next) => {
295
+ try {
296
+ if (!req.user) {
297
+ return res.status(401).json({ message: 'Authentication required' });
298
+ }
299
+
300
+ const userRole = req.user.role;
301
+
302
+ if (userRole === 'admin' || userRole === 'SuperManager') {
303
+ return next();
304
+ }
305
+
306
+ return res.status(403).json({ message: 'Access denied. Admin privileges required' });
307
+ } catch (err) {
308
+ next(err);
309
+ }
310
+ };
311
+
312
+ /**
313
+ * Middleware to validate dashboard login request body.
314
+ *
315
+ * Rules:
316
+ * - `role` must NOT be provided (role is derived server-side).
317
+ * - `email` is required.
318
+ * - `password` is required unless `google` is truthy (Google login).
319
+ *
320
+ * @param {Object} req - Express request object
321
+ * @param {Object} res - Express response object
322
+ * @param {Function} next - Express next middleware function
323
+ * @returns {void}
324
+ *
325
+ * @example
326
+ * const express = require('express');
327
+ * const { validateDashboardLogin } = require('jwt-middleware-auth');
328
+ * const router = express.Router();
329
+ *
330
+ * router.post('/dashboard/login', validateDashboardLogin, loginController);
331
+ */
332
+ const validateDashboardLogin = (req, res, next) => {
333
+ try {
334
+ const body = req.body || {};
335
+
336
+ // Role must not be provided for dashboard login
337
+ if (Object.prototype.hasOwnProperty.call(body, 'role')) {
338
+ return res.status(400).json({ message: 'Role must not be provided for dashboard login' });
339
+ }
340
+
341
+ if (!body.email) {
342
+ return res.status(400).json({ message: 'Email is required' });
343
+ }
344
+
345
+ // Password required for non-Google login
346
+ if (!body.google && !body.password) {
347
+ return res.status(400).json({ message: 'Password is required' });
348
+ }
349
+
350
+ next();
351
+ } catch (err) {
352
+ next(err);
353
+ }
354
+ };
67
355
 
68
- module.exports = { verifyToken, verifySeller, verifyAdmin, verifyTokenAndAuthorization, verifyManager };
356
+ module.exports = {
357
+ verifyToken,
358
+ authorizeRoles,
359
+ verifySeller,
360
+ verifySellerRole,
361
+ verifyTokenAndOwnership,
362
+ verifySellerOrAdmin,
363
+ verifyTokenAndMultiOwnership,
364
+ verifyUserOwnershipOrAdmin,
365
+ verifyAdminOrSuperManager,
366
+ validateDashboardLogin,
367
+ verifyAdmin,
368
+ verifyTokenAndAuthorization,
369
+ verifyManager
370
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jwt-middleware-auth",
3
- "version": "1.1.0",
4
- "description": "A flexible middleware library for JWT authentication in Express.js",
3
+ "version": "2.1.0",
4
+ "description": "A comprehensive middleware library for JWT authentication and role-based authorization in Express.js",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -10,7 +10,10 @@
10
10
  "jwt",
11
11
  "express",
12
12
  "middleware",
13
- "auth"
13
+ "auth",
14
+ "authorization",
15
+ "roles",
16
+ "rbac"
14
17
  ],
15
18
  "license": "MIT",
16
19
  "dependencies": {