jwt-middleware-auth 1.1.0 → 2.0.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 (3) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/index.js +301 -44
  3. package/package.json +6 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,149 @@
1
+ # Changelog - JWT Middleware Authentication
2
+
3
+ ## 2025-12-08
4
+
5
+ ### Added
6
+
7
+ #### User & Admin Access Control
8
+ - **`verifyUserOwnershipOrAdmin`** - Middleware for user-specific resource access control
9
+ - Validates that authenticated user can only access/modify their own data
10
+ - Grants automatic access to admin and SuperManager roles
11
+ - Extracts user ID from route params (`:id` or `:userId`)
12
+ - Returns 401 if user not authenticated
13
+ - Returns 403 if user attempts to access another user's data
14
+ - Example: `router.put('/user/:id', verifyToken(secret), verifyUserOwnershipOrAdmin, controller)`
15
+ - Use cases: User profile updates, user data retrieval, user deletion
16
+
17
+ - **`verifyAdminOrSuperManager`** - Middleware for admin-only operations
18
+ - Restricts access to users with admin or SuperManager roles
19
+ - Returns 401 if user not authenticated
20
+ - Returns 403 if user lacks admin privileges
21
+ - Example: `router.get('/users', verifyToken(secret), verifyAdminOrSuperManager, controller)`
22
+ - Use cases: Admin dashboards, user management, sensitive data endpoints
23
+
24
+ #### Seller & Multi-Role Verification
25
+ - **`verifySellerOrAdmin(secret)`** - Middleware for seller/manager/admin/SuperManager role verification
26
+ - Validates that user has one of the elevated privilege roles
27
+ - Supports: admin, seller, manager, SuperManager
28
+ - Returns 403 if user lacks required privileges
29
+ - Example: `router.get('/store/:id/orders', verifySellerOrAdmin(secret), controller)`
30
+
31
+ - **`verifySellerRole(secret)`** - Middleware for strict seller role verification
32
+ - Validates that the authenticated user has seller or paymentManager role
33
+ - Focused on role validation for creating resources (more specific than verifySeller)
34
+ - Returns 403 if user does not have required role
35
+ - Example: `router.post('/offers', verifySellerRole(secret), controller)`
36
+
37
+ #### Advanced Ownership Verification
38
+ - **`verifyTokenAndOwnership(secret, getResourceOwnerId)`** - Higher-order middleware factory for resource ownership verification
39
+ - Verifies JWT token AND checks that the authenticated user owns the resource
40
+ - Accepts async function to retrieve resource owner ID
41
+ - Handles resource not found errors with 404 status
42
+ - Compares owner ID with authenticated user ID (handles both ObjectId and string types)
43
+ - Returns 403 if user is not the resource owner
44
+ - Returns 404 if resource not found
45
+ - Returns 500 for ownership verification errors
46
+ - Example usage:
47
+ ```javascript
48
+ const checkOfferOwnership = async (req) => {
49
+ const offer = await Offer.findById(req.params.id);
50
+ if (!offer) throw new Error('Offer not found');
51
+ return offer.sellerId;
52
+ };
53
+ router.put('/offer/:id', verifyTokenAndOwnership(secret, checkOfferOwnership), controller)
54
+ ```
55
+
56
+ - **`verifyTokenAndMultiOwnership(secret, getResourceOwnerIds)`** - Advanced multi-ownership verification
57
+ - Higher-order middleware factory for resources with multiple potential owners
58
+ - Automatically grants access to admin and SuperManager roles
59
+ - Accepts async function that returns array of owner IDs
60
+ - Checks if authenticated user matches any owner ID
61
+ - Useful for complex resources like orders (client OR seller can access)
62
+ - Returns 403 if user is not an owner or admin
63
+ - Returns 404 if resource not found
64
+ - Example usage:
65
+ ```javascript
66
+ const checkOrderOwnership = async (req) => {
67
+ const order = await Order.findById(req.params.id);
68
+ if (!order) throw new Error('Order not found');
69
+ const store = await Store.findById(order.storeId);
70
+ return [order.clientId, store.sellerId]; // Both can access
71
+ };
72
+ router.get('/order/:id', verifyTokenAndMultiOwnership(secret, checkOrderOwnership), controller)
73
+ ```
74
+
75
+ #### Role-Based Access Control (RBAC)
76
+ - **`authorizeRoles(...allowedRoles)`** - Middleware function for role-based access control
77
+ - Verifies authenticated users have one of the allowed roles
78
+ - Must be used after `verifyToken` middleware
79
+ - Supports multiple roles with flexible authorization logic
80
+ - Returns 401 if user is not authenticated
81
+ - Returns 403 if user lacks required permissions
82
+ - Example: `authorizeRoles('admin', 'manager')`
83
+
84
+ #### Core Middleware (Previously Included)
85
+ - **`verifyToken(secret)`** - Middleware to verify JWT tokens from request headers
86
+ - Expects token in 'token' header as 'Bearer <token>'
87
+ - Populates `req.user` with decoded token payload
88
+
89
+ - **`verifyTokenAndAuthorization(secret)`** - Middleware for user self-update authorization
90
+ - Allows access if user is updating their own account OR is an admin
91
+
92
+ - **`verifyAdmin(secret)`** - Middleware to verify admin privileges
93
+ - Checks `req.user.isAdmin` property
94
+
95
+ - **`verifyManager(secret)`** - Middleware to verify manager role or self-access
96
+ - Allows access if user is a manager OR accessing their own resource
97
+
98
+ - **`verifySeller(secret)`** - Middleware to verify seller/payment manager role
99
+ - Allows access if user is a seller, payment manager, OR accessing their own resource
100
+
101
+ ### Changed
102
+ - Updated package description to reflect role-based authorization capabilities
103
+ - Enhanced JSDoc documentation for all middleware functions with detailed examples
104
+ - Added comprehensive inline comments explaining middleware behavior
105
+ - Exported all new middleware functions in module exports
106
+ - Package keywords updated to include: "authorization", "roles", "rbac"
107
+
108
+ ### Improved
109
+ - Enhanced user data protection with ownership verification
110
+ - Centralized admin role verification for consistent access control
111
+ - Better separation of concerns between user-level and admin-level operations
112
+ - Enhanced support for multi-tenant authorization patterns
113
+ - Better handling of resources with multiple owners or access levels
114
+ - Consistent admin/SuperManager privilege handling across all ownership middlewares
115
+ - Enhanced flexibility for seller-related authorization with separate role-checking middleware
116
+ - Better separation of concerns between role verification and ownership verification
117
+ - Comprehensive error handling for resource ownership validation
118
+ - Better error handling in `authorizeRoles` middleware
119
+ - More descriptive error messages for authentication and authorization failures
120
+
121
+ ---
122
+
123
+ ## Summary
124
+
125
+ **Total Middleware Functions:** 13
126
+
127
+ **Categories:**
128
+ - Core Authentication: 1 (verifyToken)
129
+ - Admin & Role Verification: 4 (verifyAdmin, verifyAdminOrSuperManager, authorizeRoles, verifyManager)
130
+ - Seller Verification: 3 (verifySeller, verifySellerRole, verifySellerOrAdmin)
131
+ - Ownership & Authorization: 3 (verifyTokenAndAuthorization, verifyTokenAndOwnership, verifyTokenAndMultiOwnership)
132
+ - User Access Control: 1 (verifyUserOwnershipOrAdmin)
133
+
134
+ **Key Features:**
135
+ - ✅ Complete JWT token verification
136
+ - ✅ Flexible role-based access control (RBAC)
137
+ - ✅ Resource ownership verification (single & multi-owner)
138
+ - ✅ User self-access & admin privilege management
139
+ - ✅ Seller/Manager role validation
140
+ - ✅ Comprehensive error handling
141
+ - ✅ TypeScript-ready with JSDoc documentation
142
+
143
+ **Dependencies:**
144
+ - jsonwebtoken: ^9.0.3
145
+
146
+ **Migration Notes:**
147
+ - All changes are additive - existing code continues to work
148
+ - Custom authorization middleware can be replaced with centralized package functions
149
+ - Simply import new middleware functions alongside existing ones for enhanced capabilities
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,254 @@ 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
+ });
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
+ });
66
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
+ };
67
311
 
68
- module.exports = { verifyToken, verifySeller, verifyAdmin, verifyTokenAndAuthorization, verifyManager };
312
+ module.exports = {
313
+ verifyToken,
314
+ authorizeRoles,
315
+ verifySeller,
316
+ verifySellerRole,
317
+ verifyTokenAndOwnership,
318
+ verifySellerOrAdmin,
319
+ verifyTokenAndMultiOwnership,
320
+ verifyUserOwnershipOrAdmin,
321
+ verifyAdminOrSuperManager,
322
+ verifyAdmin,
323
+ verifyTokenAndAuthorization,
324
+ verifyManager
325
+ };
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.0.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": {