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.
- package/CHANGELOG.md +147 -0
- package/README.md +527 -0
- package/index.js +346 -44
- 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 = {
|
|
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": "
|
|
4
|
-
"description": "A
|
|
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": {
|