@javagt/express-easy-auth 1.0.0 → 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/package.json +1 -1
- package/readme.md +16 -0
- package/src/client.js +10 -3
- package/src/middleware/auth.js +1 -1
- package/src/routes/auth.js +31 -2
- package/src/utils/authHelpers.js +1 -1
- package/src/utils/logger.js +1 -1
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -106,6 +106,20 @@ The library provides a flexible logging system and explicit control over error e
|
|
|
106
106
|
- **Custom Logger**: Plug in your own logger (e.g., Winston, Pino) by passing it to `setupAuth`.
|
|
107
107
|
- [View Example: Custom Logger](examples/05-custom-logger.js)
|
|
108
108
|
|
|
109
|
+
### 🌐 SPA Fallback (Modern Alternative to Wildcard Routes)
|
|
110
|
+
When building Single Page Applications (SPAs), avoid using catch-all wildcard routes (e.g., `app.get('*', ...)`) as they can cause routing conflicts and `PathError` in newer versions of Express.
|
|
111
|
+
|
|
112
|
+
Instead, use a middleware-based fallback that only triggers for HTML requests:
|
|
113
|
+
```javascript
|
|
114
|
+
app.use((req, res, next) => {
|
|
115
|
+
if (req.method === 'GET' && req.accepts('html') && !req.path.startsWith('/api')) {
|
|
116
|
+
res.sendFile(path.join(__dirname, 'public/index.html'));
|
|
117
|
+
} else {
|
|
118
|
+
next();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
109
123
|
---
|
|
110
124
|
|
|
111
125
|
## 📜 API Reference
|
|
@@ -128,6 +142,8 @@ All identity endpoints are nested under the router (recommended path: `/api/v1/a
|
|
|
128
142
|
| | POST | `/passkeys/authenticate/verify` | — | Verify passkey login |
|
|
129
143
|
| | GET | `/passkeys` | ✓ | List registered passkeys |
|
|
130
144
|
| | DELETE | `/passkeys/:id` | ✓ | Delete a passkey |
|
|
145
|
+
| **Account** | POST | `/password/change` | ✓ (Fresh) | Change current password |
|
|
146
|
+
| | POST | `/email/change` | ✓ (Fresh) | Update email address |
|
|
131
147
|
| **Password Reset** | POST | `/password-reset/request` | — | Generate reset token |
|
|
132
148
|
| | POST | `/password-reset/reset` | — | Complete reset |
|
|
133
149
|
| **API Keys** | GET | `/api-keys` | ✓ | List user API keys |
|
package/src/client.js
CHANGED
|
@@ -102,10 +102,17 @@ export class AuthClient {
|
|
|
102
102
|
return this.request('/logout', { method: 'POST' });
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
async changePassword(newPassword
|
|
106
|
-
return this.request('/change
|
|
105
|
+
async changePassword(newPassword) {
|
|
106
|
+
return this.request('/password/change', {
|
|
107
107
|
method: 'POST',
|
|
108
|
-
body: { newPassword
|
|
108
|
+
body: { newPassword }
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async changeEmail(newEmail) {
|
|
113
|
+
return this.request('/email/change', {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
body: { newEmail }
|
|
109
116
|
});
|
|
110
117
|
}
|
|
111
118
|
|
package/src/middleware/auth.js
CHANGED
package/src/routes/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import bcrypt from 'bcrypt';
|
|
3
|
-
import { randomUUID, randomBytes } from 'crypto';
|
|
3
|
+
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
4
|
import { verifySync, generateSecret } from 'otplib';
|
|
5
5
|
import QRCode from 'qrcode';
|
|
6
6
|
import {
|
|
@@ -290,7 +290,9 @@ router.post('/2fa/setup', requireAuth, async (req, res) => {
|
|
|
290
290
|
const secret = generateSecret();
|
|
291
291
|
req.session.pendingTotpSecret = secret;
|
|
292
292
|
|
|
293
|
-
const
|
|
293
|
+
const { rpName } = getRpConfig(req);
|
|
294
|
+
const issuer = encodeURIComponent(rpName);
|
|
295
|
+
const otpauthUrl = `otpauth://totp/${issuer}:${encodeURIComponent(user.email)}?secret=${secret}&issuer=${issuer}`;
|
|
294
296
|
const qrCode = await QRCode.toDataURL(otpauthUrl);
|
|
295
297
|
|
|
296
298
|
res.json({ secret, qrCode, otpauthUrl });
|
|
@@ -449,6 +451,33 @@ router.delete('/passkeys/:id', requireAuth, (req, res) => {
|
|
|
449
451
|
res.json({ success: true });
|
|
450
452
|
});
|
|
451
453
|
|
|
454
|
+
// ─── ACCOUNT MANAGEMENT ──────────────────────────────────────────────────────
|
|
455
|
+
|
|
456
|
+
router.post('/password/change', requireFreshAuth, async (req, res) => {
|
|
457
|
+
const { newPassword } = req.body;
|
|
458
|
+
if (!newPassword) return res.status(400).json({ error: 'newPassword is required' });
|
|
459
|
+
|
|
460
|
+
const settings = getAppSettings();
|
|
461
|
+
const minLen = parseInt(settings.password_min_length || '8', 10);
|
|
462
|
+
if (newPassword.length < minLen) {
|
|
463
|
+
return res.status(400).json({ error: `Password must be at least ${minLen} characters` });
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const hash = await bcrypt.hash(newPassword, SALT_ROUNDS);
|
|
467
|
+
authDb.prepare('UPDATE users SET password_hash=?, updated_at=? WHERE id=?').run(hash, Date.now(), req.userId);
|
|
468
|
+
|
|
469
|
+
res.json({ success: true, message: 'Password changed successfully' });
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
router.post('/email/change', requireFreshAuth, async (req, res) => {
|
|
473
|
+
const { newEmail } = req.body;
|
|
474
|
+
if (!newEmail) return res.status(400).json({ error: 'newEmail is required' });
|
|
475
|
+
|
|
476
|
+
authDb.prepare('UPDATE users SET email=?, updated_at=? WHERE id=?').run(newEmail, Date.now(), req.userId);
|
|
477
|
+
|
|
478
|
+
res.json({ success: true, message: 'Email updated successfully', email: newEmail });
|
|
479
|
+
});
|
|
480
|
+
|
|
452
481
|
// ─── SESSIONS ────────────────────────────────────────────────────────────────
|
|
453
482
|
|
|
454
483
|
router.get('/sessions', requireAuth, (req, res) => {
|
package/src/utils/authHelpers.js
CHANGED
package/src/utils/logger.js
CHANGED