@latanda/auth-middleware 1.0.1
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 +15 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +29 -0
- package/README.md +438 -0
- package/examples/basic-usage.js +90 -0
- package/package.json +66 -0
- package/sql/schema.sql +126 -0
- package/src/index.js +43 -0
- package/src/jwt.js +186 -0
- package/src/middleware.js +256 -0
- package/src/rbac.js +180 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.1 - 2026-05-12
|
|
4
|
+
|
|
5
|
+
- Renamed scope: `@perks/auth-middleware` → `@latanda/auth-middleware`. The old package is deprecated with a redirect message.
|
|
6
|
+
- **Fixed broken `main` field**: 1.0.0 pointed at `lib/index.js` which was never built or committed (`lib/` is gitignored). Changed to `src/index.js` so `require("@latanda/auth-middleware")` actually works. The old `@perks` 1.0.0 was effectively unusable due to this bug.
|
|
7
|
+
- Added explicit `files` field to package.json for predictable npm tarballs.
|
|
8
|
+
- Removed stale `types` field (it pointed at the same nonexistent `lib/index.d.ts`).
|
|
9
|
+
- Fixed author email metadata (was a placeholder).
|
|
10
|
+
- Added npm version, downloads, license, and node badges to README.
|
|
11
|
+
- No API changes.
|
|
12
|
+
|
|
13
|
+
## 1.0.0 - 2025-10-22
|
|
14
|
+
|
|
15
|
+
- Initial release. JWT auth, RBAC, PostgreSQL integration, Nginx-friendly. Battle-tested at latanda.online.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Narjell Ebanks / La Tanda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Migrating from @perks/auth-middleware to @latanda/auth-middleware
|
|
2
|
+
|
|
3
|
+
The package was renamed in v1.0.1 to align with the La Tanda project brand. No code changes are required other than updating the package name in your dependencies.
|
|
4
|
+
|
|
5
|
+
## Quick upgrade
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm uninstall @perks/auth-middleware
|
|
9
|
+
npm install @latanda/auth-middleware
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## require() / import changes
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// Before
|
|
16
|
+
const auth = require('@perks/auth-middleware');
|
|
17
|
+
// After
|
|
18
|
+
const auth = require('@latanda/auth-middleware');
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The exported API is identical between 1.0.0 and 1.0.1.
|
|
22
|
+
|
|
23
|
+
## Why the rename?
|
|
24
|
+
|
|
25
|
+
`@perks` was a personal npm scope. `@latanda` is the project scope used across the La Tanda ecosystem (whitepaper, platform, future packages like `@latanda/cv-refiner` and `@latanda/tanda-engine`). Aligning the package name with the brand prevents confusion and makes future ecosystem packages discoverable under one scope.
|
|
26
|
+
|
|
27
|
+
## Deprecation of @perks/auth-middleware
|
|
28
|
+
|
|
29
|
+
`@perks/auth-middleware@1.0.0` will remain on the registry but is deprecated with a message pointing to this package. Existing installs will continue to work; please migrate at your convenience.
|
package/README.md
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
# @latanda/auth-middleware
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@latanda/auth-middleware)
|
|
4
|
+
[](https://www.npmjs.com/package/@latanda/auth-middleware)
|
|
5
|
+
[](https://github.com/INDIGOAZUL/latanda-auth-middleware/blob/main/LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
|
|
8
|
+
[](https://github.com/INDIGOAZUL/latanda-auth-middleware/actions/workflows/test.yml)
|
|
9
|
+
|
|
10
|
+
**Production-ready JWT authentication middleware for Node.js + PostgreSQL + Nginx**
|
|
11
|
+
|
|
12
|
+
Battle-tested with 30+ users at [latanda.online](https://latanda.online) ✨
|
|
13
|
+
|
|
14
|
+
## Migration from @perks/auth-middleware
|
|
15
|
+
|
|
16
|
+
This package was previously published as `@perks/auth-middleware`. The scope was renamed to `@latanda` in v1.0.1 to align with the La Tanda project brand. The API is unchanged — only the package name and import path differ. See [MIGRATION.md](MIGRATION.md) for full details.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm uninstall @perks/auth-middleware && npm install @latanda/auth-middleware
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
// Before: const auth = require('@perks/auth-middleware');
|
|
24
|
+
// After: const auth = require('@latanda/auth-middleware');
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- ✅ **JWT Token Generation & Validation** - Secure HS256 tokens with comprehensive claim validation
|
|
30
|
+
- ✅ **Role-Based Access Control (RBAC)** - Pre-configured roles: ADMIN, MIT, IT, USER
|
|
31
|
+
- ✅ **Express Middleware** - Drop-in authentication for Express.js apps
|
|
32
|
+
- ✅ **PostgreSQL Integration** - Production-ready database schema included
|
|
33
|
+
- ✅ **Nginx Compatible** - Works seamlessly with Nginx reverse proxy
|
|
34
|
+
- ✅ **Token Refresh** - Automatic token refresh with expiration detection
|
|
35
|
+
- ✅ **Permission System** - Granular permission checks beyond roles
|
|
36
|
+
- ✅ **Resource Ownership** - Enforce users can only access their own resources
|
|
37
|
+
- ✅ **Audit Logging** - Track authentication events for security monitoring
|
|
38
|
+
- ✅ **Zero Dependencies** - Only requires `jsonwebtoken`, `bcrypt`, and `pg`
|
|
39
|
+
|
|
40
|
+
## Quick Start (5 minutes to secure auth)
|
|
41
|
+
|
|
42
|
+
### Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install @latanda/auth-middleware jsonwebtoken bcrypt pg
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 1. Set up PostgreSQL
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Run the schema migration
|
|
52
|
+
psql -U your_user -d your_database -f node_modules/@latanda/auth-middleware/sql/schema.sql
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. Add to your Express app
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const express = require('express');
|
|
59
|
+
const { createAuthMiddleware, requireRole } = require('@latanda/auth-middleware');
|
|
60
|
+
|
|
61
|
+
const app = express();
|
|
62
|
+
|
|
63
|
+
// Protect all API routes
|
|
64
|
+
app.use('/api/*', createAuthMiddleware({
|
|
65
|
+
jwtSecret: process.env.JWT_SECRET // Store this in .env file!
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
// Require ADMIN role for admin routes
|
|
69
|
+
app.use('/api/admin/*', requireRole('ADMIN'));
|
|
70
|
+
|
|
71
|
+
// Your protected routes
|
|
72
|
+
app.get('/api/profile', (req, res) => {
|
|
73
|
+
// req.user is automatically populated with authenticated user
|
|
74
|
+
res.json({
|
|
75
|
+
success: true,
|
|
76
|
+
user: req.user // { id, email, role, permissions }
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
app.listen(3000, () => console.log('Secure API running on port 3000'));
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Generate tokens on login
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const { generateToken } = require('@latanda/auth-middleware');
|
|
87
|
+
const bcrypt = require('bcrypt');
|
|
88
|
+
|
|
89
|
+
app.post('/auth/login', async (req, res) => {
|
|
90
|
+
const { email, password } = req.body;
|
|
91
|
+
|
|
92
|
+
// Get user from database
|
|
93
|
+
const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
|
94
|
+
|
|
95
|
+
if (!user || !await bcrypt.compare(password, user.password_hash)) {
|
|
96
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Generate JWT token
|
|
100
|
+
const token = generateToken(user, process.env.JWT_SECRET);
|
|
101
|
+
|
|
102
|
+
res.json({
|
|
103
|
+
success: true,
|
|
104
|
+
token,
|
|
105
|
+
user: { id: user.id, email: user.email, role: user.role }
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**That's it!** Your API is now secured with production-ready JWT authentication.
|
|
111
|
+
|
|
112
|
+
## Case Study: La Tanda
|
|
113
|
+
|
|
114
|
+
[La Tanda](https://latanda.online) is a Web3 tanda (rotating savings and credit association) platform serving 30+ users across 16+ groups with real financial transactions.
|
|
115
|
+
|
|
116
|
+
**What we use this middleware for:**
|
|
117
|
+
|
|
118
|
+
- ✅ **Secure user authentication** - JWT tokens with 8-hour expiration
|
|
119
|
+
- ✅ **Role-based access** - ADMIN (platform management), MIT (group coordinators), USER (members)
|
|
120
|
+
- ✅ **API protection** - All 85+ API endpoints secured
|
|
121
|
+
- ✅ **Transaction security** - Ensure users can only access their own transactions
|
|
122
|
+
- ✅ **Admin panel** - Restricted to ADMIN role with full audit logging
|
|
123
|
+
|
|
124
|
+
**Results:**
|
|
125
|
+
- 🔒 Zero authentication-related security incidents
|
|
126
|
+
- ⚡ Sub-100ms JWT validation performance
|
|
127
|
+
- 📊 Complete audit trail of all auth events
|
|
128
|
+
- 🚀 Seamless integration with Nginx reverse proxy
|
|
129
|
+
|
|
130
|
+
## Full Documentation
|
|
131
|
+
|
|
132
|
+
### API Reference
|
|
133
|
+
|
|
134
|
+
#### JWT Functions
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
const { generateToken, validateToken, refreshToken } = require('@latanda/auth-middleware');
|
|
138
|
+
|
|
139
|
+
// Generate a token
|
|
140
|
+
const token = generateToken(user, jwtSecret, {
|
|
141
|
+
expiresIn: '8h', // Default: 8 hours
|
|
142
|
+
issuer: 'your-app.com',
|
|
143
|
+
audience: 'your-app'
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Validate a token
|
|
147
|
+
const validation = validateToken(token, jwtSecret);
|
|
148
|
+
if (validation.valid) {
|
|
149
|
+
console.log('User:', validation.user_id, validation.email, validation.role);
|
|
150
|
+
} else {
|
|
151
|
+
console.error('Invalid token:', validation.error);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Refresh a token (if expiring soon)
|
|
155
|
+
const refreshResult = refreshToken(oldToken, jwtSecret);
|
|
156
|
+
if (refreshResult.success) {
|
|
157
|
+
console.log('New token:', refreshResult.token);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### RBAC Functions
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const { hasPermission, hasRoleLevel, ROLES } = require('@latanda/auth-middleware');
|
|
165
|
+
|
|
166
|
+
// Check if user has permission
|
|
167
|
+
if (hasPermission('ADMIN', 'user_management')) {
|
|
168
|
+
// User has permission
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check if role meets minimum level
|
|
172
|
+
if (hasRoleLevel('MIT', 'USER')) { // true - MIT is higher than USER
|
|
173
|
+
// Access granted
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// View all roles and permissions
|
|
177
|
+
console.log(ROLES);
|
|
178
|
+
/*
|
|
179
|
+
{
|
|
180
|
+
ADMIN: { level: 100, permissions: [...] },
|
|
181
|
+
MIT: { level: 50, permissions: [...] },
|
|
182
|
+
IT: { level: 75, permissions: [...] },
|
|
183
|
+
USER: { level: 10, permissions: [...] }
|
|
184
|
+
}
|
|
185
|
+
*/
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Middleware
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const {
|
|
192
|
+
createAuthMiddleware,
|
|
193
|
+
requirePermission,
|
|
194
|
+
requireRole,
|
|
195
|
+
requireOwnership,
|
|
196
|
+
optionalAuth
|
|
197
|
+
} = require('@latanda/auth-middleware');
|
|
198
|
+
|
|
199
|
+
// Basic authentication (required)
|
|
200
|
+
app.use('/api/*', createAuthMiddleware({ jwtSecret: process.env.JWT_SECRET }));
|
|
201
|
+
|
|
202
|
+
// Require specific permission
|
|
203
|
+
app.post('/api/users', requirePermission('user_management'), (req, res) => {
|
|
204
|
+
// Only users with 'user_management' permission can access
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Require specific role or higher
|
|
208
|
+
app.use('/api/admin/*', requireRole('ADMIN'));
|
|
209
|
+
app.use('/api/groups/create', requireRole('MIT')); // MIT or ADMIN
|
|
210
|
+
|
|
211
|
+
// Enforce resource ownership
|
|
212
|
+
app.get('/api/transactions/:userId', requireOwnership(req => req.params.userId), (req, res) => {
|
|
213
|
+
// Users can only view their own transactions (unless ADMIN)
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Optional authentication (works with or without token)
|
|
217
|
+
app.get('/api/public/stats', optionalAuth({ jwtSecret: process.env.JWT_SECRET }), (req, res) => {
|
|
218
|
+
if (req.user) {
|
|
219
|
+
// Show personalized stats
|
|
220
|
+
} else {
|
|
221
|
+
// Show public stats
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Roles and Permissions
|
|
227
|
+
|
|
228
|
+
#### Pre-configured Roles
|
|
229
|
+
|
|
230
|
+
| Role | Level | Permissions |
|
|
231
|
+
|------|-------|-------------|
|
|
232
|
+
| **ADMIN** | 100 | `full_access`, `user_management`, `system_config`, `approve_deposits`, `manage_groups`, `view_analytics`, `manage_roles`, `delete_users`, `system_settings` |
|
|
233
|
+
| **IT** | 75 | `view_system_logs`, `debug_access`, `api_access`, `view_analytics`, `technical_support` |
|
|
234
|
+
| **MIT** | 50 | `create_groups`, `manage_own_groups`, `approve_members`, `view_group_analytics`, `edit_group_settings` |
|
|
235
|
+
| **USER** | 10 | `view_own_profile`, `edit_own_profile`, `join_groups`, `make_payments`, `view_own_transactions` |
|
|
236
|
+
|
|
237
|
+
#### Custom Permissions
|
|
238
|
+
|
|
239
|
+
You can add custom permissions to the database:
|
|
240
|
+
|
|
241
|
+
```sql
|
|
242
|
+
INSERT INTO user_permissions (user_id, permission, granted_by)
|
|
243
|
+
VALUES (123, 'beta_access', 1);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Then check in your code:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
if (req.user.permissions.includes('beta_access')) {
|
|
250
|
+
// Show beta features
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Database Schema
|
|
255
|
+
|
|
256
|
+
The included PostgreSQL schema provides:
|
|
257
|
+
|
|
258
|
+
- ✅ **users** table - User accounts with bcrypt password hashes
|
|
259
|
+
- ✅ **sessions** table - Active JWT token tracking (optional, for revocation)
|
|
260
|
+
- ✅ **user_permissions** table - Custom per-user permissions
|
|
261
|
+
- ✅ **auth_audit_log** table - Authentication event audit trail
|
|
262
|
+
|
|
263
|
+
See `sql/schema.sql` for full details.
|
|
264
|
+
|
|
265
|
+
### Nginx Integration
|
|
266
|
+
|
|
267
|
+
This middleware works seamlessly with Nginx reverse proxy:
|
|
268
|
+
|
|
269
|
+
```nginx
|
|
270
|
+
# /etc/nginx/sites-available/your-app
|
|
271
|
+
location /api/ {
|
|
272
|
+
proxy_pass http://localhost:3000/api/;
|
|
273
|
+
proxy_set_header Host $host;
|
|
274
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
275
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
276
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
277
|
+
|
|
278
|
+
# CORS headers for JWT
|
|
279
|
+
add_header Access-Control-Allow-Origin * always;
|
|
280
|
+
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
|
|
281
|
+
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
|
|
282
|
+
|
|
283
|
+
if ($request_method = OPTIONS) {
|
|
284
|
+
return 204;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Then your frontend can make authenticated requests:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
fetch('https://your-app.com/api/profile', {
|
|
293
|
+
headers: {
|
|
294
|
+
'Authorization': `Bearer ${token}`,
|
|
295
|
+
'Content-Type': 'application/json'
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Advanced Usage
|
|
301
|
+
|
|
302
|
+
### Custom Unauthorized Handler
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
app.use('/api/*', createAuthMiddleware({
|
|
306
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
307
|
+
onUnauthorized: (req, res, error) => {
|
|
308
|
+
// Custom error handling
|
|
309
|
+
console.error('Auth failed:', error);
|
|
310
|
+
res.status(401).json({
|
|
311
|
+
success: false,
|
|
312
|
+
error: 'Custom error message',
|
|
313
|
+
code: error.code
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}));
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Token Refresh Strategy
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
const { isTokenExpiringSoon, refreshToken } = require('@latanda/auth-middleware');
|
|
323
|
+
|
|
324
|
+
app.post('/api/refresh-token', (req, res) => {
|
|
325
|
+
const token = req.headers.authorization?.substring(7);
|
|
326
|
+
|
|
327
|
+
if (isTokenExpiringSoon(token, 15)) { // Refresh if expires within 15 min
|
|
328
|
+
const result = refreshToken(token, process.env.JWT_SECRET);
|
|
329
|
+
if (result.success) {
|
|
330
|
+
return res.json({ success: true, token: result.token });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
res.json({ success: false, error: 'Token not eligible for refresh' });
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Group Ownership Example (La Tanda use case)
|
|
339
|
+
|
|
340
|
+
```javascript
|
|
341
|
+
const { canPerformGroupAction } = require('@latanda/auth-middleware');
|
|
342
|
+
|
|
343
|
+
app.post('/api/groups/:groupId/approve-member', async (req, res) => {
|
|
344
|
+
const group = await db.query('SELECT * FROM groups WHERE id = $1', [req.params.groupId]);
|
|
345
|
+
|
|
346
|
+
// Check if user can approve members in this group
|
|
347
|
+
if (!canPerformGroupAction(req.user.id, group, req.user.role, 'approve_members')) {
|
|
348
|
+
return res.status(403).json({ error: 'Not authorized to approve members' });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Approve member...
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Examples
|
|
356
|
+
|
|
357
|
+
See the `examples/` directory for complete working examples:
|
|
358
|
+
|
|
359
|
+
- **basic-usage.js** - Simple Express app with JWT auth
|
|
360
|
+
- **role-based-routing.js** - Different routes for different roles
|
|
361
|
+
- **refresh-token-flow.js** - Automatic token refresh implementation
|
|
362
|
+
- **nginx-integration/** - Complete Nginx + Express + PostgreSQL setup
|
|
363
|
+
|
|
364
|
+
## Testing
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
npm test
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Security Considerations
|
|
371
|
+
|
|
372
|
+
🔒 **Always use HTTPS in production** - JWT tokens are bearer tokens and should be transmitted securely
|
|
373
|
+
|
|
374
|
+
🔒 **Store JWT_SECRET securely** - Use environment variables, never commit secrets
|
|
375
|
+
|
|
376
|
+
🔒 **Set appropriate token expiration** - Balance security (shorter) vs user experience (longer)
|
|
377
|
+
|
|
378
|
+
🔒 **Implement token refresh** - Allow users to stay logged in without re-authentication
|
|
379
|
+
|
|
380
|
+
🔒 **Monitor audit logs** - Watch for suspicious authentication patterns
|
|
381
|
+
|
|
382
|
+
🔒 **Use bcrypt for passwords** - Never store plain-text passwords
|
|
383
|
+
|
|
384
|
+
## 💰 Support This Project
|
|
385
|
+
|
|
386
|
+
If this package saves you time and helps secure your application, consider supporting its development!
|
|
387
|
+
|
|
388
|
+
[](https://ko-fi.com/ebanks)
|
|
389
|
+
[](https://paypal.me/narjell)
|
|
390
|
+
[](https://opencollective.com/latanda-auth-middleware)
|
|
391
|
+
|
|
392
|
+
**Why support?**
|
|
393
|
+
- ✅ Ongoing maintenance and security updates
|
|
394
|
+
- ✅ New features based on community feedback
|
|
395
|
+
- ✅ Comprehensive documentation and examples
|
|
396
|
+
- ✅ Priority support for sponsors
|
|
397
|
+
|
|
398
|
+
**Other ways to help:**
|
|
399
|
+
- ⭐ Star this repo on GitHub
|
|
400
|
+
- 🐛 Report bugs and suggest features
|
|
401
|
+
- 📝 Improve documentation
|
|
402
|
+
- 🔀 Submit pull requests
|
|
403
|
+
|
|
404
|
+
### 💼 Need Help?
|
|
405
|
+
|
|
406
|
+
**Professional services available:**
|
|
407
|
+
- 📧 Implementation consulting
|
|
408
|
+
- 🔧 Custom feature development
|
|
409
|
+
- 🏢 Enterprise support contracts
|
|
410
|
+
|
|
411
|
+
Contact: ebanksnigel@gmail.com
|
|
412
|
+
|
|
413
|
+
## Contributing
|
|
414
|
+
|
|
415
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
416
|
+
|
|
417
|
+
## License
|
|
418
|
+
|
|
419
|
+
MIT © Narjell Ebanks
|
|
420
|
+
|
|
421
|
+
## Support
|
|
422
|
+
|
|
423
|
+
- 📖 [Full Documentation](https://github.com/INDIGOAZUL/latanda-auth-middleware/wiki)
|
|
424
|
+
- 🐛 [Report Issues](https://github.com/INDIGOAZUL/latanda-auth-middleware/issues)
|
|
425
|
+
- 💬 [Discussions](https://github.com/INDIGOAZUL/latanda-auth-middleware/discussions)
|
|
426
|
+
- ⭐ [Star on GitHub](https://github.com/INDIGOAZUL/latanda-auth-middleware)
|
|
427
|
+
|
|
428
|
+
## Acknowledgments
|
|
429
|
+
|
|
430
|
+
Extracted from the [La Tanda](https://latanda.online) Web3 platform - a production tanda (ROSCA) system serving real users with real money.
|
|
431
|
+
|
|
432
|
+
Special thanks to the La Tanda community for battle-testing this authentication system!
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
**Built with ❤️ by the La Tanda team**
|
|
437
|
+
|
|
438
|
+
[GitHub](https://github.com/INDIGOAZUL/latanda-auth-middleware) • [npm](https://www.npmjs.com/package/@latanda/auth-middleware) • [La Tanda Platform](https://latanda.online)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Usage Example for @latanda/auth-middleware
|
|
3
|
+
*
|
|
4
|
+
* This example shows how to set up a simple Express server
|
|
5
|
+
* with JWT authentication in under 50 lines of code.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const express = require('express');
|
|
9
|
+
const {
|
|
10
|
+
createAuthMiddleware,
|
|
11
|
+
generateToken,
|
|
12
|
+
requireRole
|
|
13
|
+
} = require('@latanda/auth-middleware');
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
app.use(express.json());
|
|
17
|
+
|
|
18
|
+
// Configuration
|
|
19
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-this-in-production';
|
|
20
|
+
|
|
21
|
+
// ===== PUBLIC ROUTES =====
|
|
22
|
+
|
|
23
|
+
// Health check (no auth required)
|
|
24
|
+
app.get('/health', (req, res) => {
|
|
25
|
+
res.json({ status: 'ok', authenticated: false });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Login endpoint (generates JWT token)
|
|
29
|
+
app.post('/auth/login', (req, res) => {
|
|
30
|
+
const { email, password } = req.body;
|
|
31
|
+
|
|
32
|
+
// TODO: Validate credentials against database
|
|
33
|
+
// For demo purposes, we'll accept any email/password
|
|
34
|
+
const user = {
|
|
35
|
+
id: '123',
|
|
36
|
+
email,
|
|
37
|
+
role: 'USER',
|
|
38
|
+
permissions: []
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Generate JWT token
|
|
42
|
+
const token = generateToken(user, JWT_SECRET, {
|
|
43
|
+
expiresIn: '8h'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
res.json({
|
|
47
|
+
success: true,
|
|
48
|
+
token,
|
|
49
|
+
user: { id: user.id, email: user.email, role: user.role }
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ===== PROTECTED ROUTES =====
|
|
54
|
+
|
|
55
|
+
// Apply authentication middleware to all /api/* routes
|
|
56
|
+
app.use('/api/*', createAuthMiddleware({
|
|
57
|
+
jwtSecret: JWT_SECRET
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Protected profile route (any authenticated user)
|
|
61
|
+
app.get('/api/profile', (req, res) => {
|
|
62
|
+
res.json({
|
|
63
|
+
success: true,
|
|
64
|
+
user: req.user // Automatically populated by middleware
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Protected route for ADMIN only
|
|
69
|
+
app.get('/api/admin/users', requireRole('ADMIN'), (req, res) => {
|
|
70
|
+
res.json({
|
|
71
|
+
success: true,
|
|
72
|
+
message: 'Admin access granted',
|
|
73
|
+
users: [] // TODO: Fetch from database
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ===== START SERVER =====
|
|
78
|
+
|
|
79
|
+
const PORT = process.env.PORT || 3000;
|
|
80
|
+
app.listen(PORT, () => {
|
|
81
|
+
console.log(`✅ Server running on http://localhost:${PORT}`);
|
|
82
|
+
console.log(`\n📝 Try these commands:\n`);
|
|
83
|
+
console.log(`# 1. Login to get token`);
|
|
84
|
+
console.log(`curl -X POST http://localhost:${PORT}/auth/login \\`);
|
|
85
|
+
console.log(` -H "Content-Type: application/json" \\`);
|
|
86
|
+
console.log(` -d '{"email":"test@example.com","password":"test123"}'\n`);
|
|
87
|
+
console.log(`# 2. Use token to access protected route`);
|
|
88
|
+
console.log(`curl http://localhost:${PORT}/api/profile \\`);
|
|
89
|
+
console.log(` -H "Authorization: Bearer <TOKEN_FROM_STEP_1>"\n`);
|
|
90
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@latanda/auth-middleware",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Production-ready JWT authentication middleware for Node.js + PostgreSQL + Nginx. Battle-tested with 30+ users at latanda.online",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"sql",
|
|
9
|
+
"examples",
|
|
10
|
+
"README.md",
|
|
11
|
+
"CHANGELOG.md",
|
|
12
|
+
"MIGRATION.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"example": "node examples/basic-usage.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"authentication",
|
|
21
|
+
"jwt",
|
|
22
|
+
"postgresql",
|
|
23
|
+
"nginx",
|
|
24
|
+
"sessions",
|
|
25
|
+
"middleware",
|
|
26
|
+
"express",
|
|
27
|
+
"security",
|
|
28
|
+
"rbac",
|
|
29
|
+
"role-based-access-control",
|
|
30
|
+
"latanda",
|
|
31
|
+
"production-ready"
|
|
32
|
+
],
|
|
33
|
+
"author": "Narjell Ebanks <ebanksnigel@gmail.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/INDIGOAZUL/latanda-auth-middleware.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/INDIGOAZUL/latanda-auth-middleware/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/INDIGOAZUL/latanda-auth-middleware#readme",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"jsonwebtoken": "^9.0.2",
|
|
48
|
+
"bcrypt": "^5.1.1",
|
|
49
|
+
"pg": "^8.11.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^20.10.0",
|
|
53
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
54
|
+
"@types/bcrypt": "^5.0.2",
|
|
55
|
+
"@types/pg": "^8.10.9",
|
|
56
|
+
"typescript": "^5.3.0",
|
|
57
|
+
"jest": "^29.7.0",
|
|
58
|
+
"@types/jest": "^29.5.11"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"express": "^4.18.0 || ^5.0.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=16.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|