@veloxts/auth 0.7.0 → 0.7.2
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 +18 -0
- package/dist/hash.js +4 -2
- package/dist/jwt.js +4 -2
- package/dist/middleware.js +12 -31
- package/dist/password-policy.js +3 -1
- package/dist/plugin.d.ts +1 -2
- package/dist/policies.js +3 -1
- package/dist/rate-limit.js +2 -6
- package/dist/session.js +31 -77
- package/dist/token-store.js +3 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @veloxts/auth
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- chore(auth,core,create,cli,client,orm,mcp,router,validation,web): simplify code for clarity and maintainability
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @veloxts/core@0.7.2
|
|
10
|
+
- @veloxts/router@0.7.2
|
|
11
|
+
|
|
12
|
+
## 0.7.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- security audit, bumps dependency packages
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @veloxts/core@0.7.1
|
|
19
|
+
- @veloxts/router@0.7.1
|
|
20
|
+
|
|
3
21
|
## 0.7.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/dist/hash.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
|
+
import { createLogger } from '@veloxts/core';
|
|
8
|
+
const log = createLogger('auth');
|
|
7
9
|
const scryptAsync = promisify(scrypt);
|
|
8
10
|
// ============================================================================
|
|
9
11
|
// Constants
|
|
@@ -134,7 +136,7 @@ export class PasswordHasher {
|
|
|
134
136
|
catch (error) {
|
|
135
137
|
// Fallback to scrypt if bcrypt fails
|
|
136
138
|
if (error.message.includes('not found')) {
|
|
137
|
-
|
|
139
|
+
log.warn('bcrypt not available, falling back to scrypt');
|
|
138
140
|
return this.hashWithScrypt(password);
|
|
139
141
|
}
|
|
140
142
|
throw error;
|
|
@@ -163,7 +165,7 @@ export class PasswordHasher {
|
|
|
163
165
|
catch (error) {
|
|
164
166
|
// Fallback to scrypt if argon2 fails
|
|
165
167
|
if (error.message.includes('not found')) {
|
|
166
|
-
|
|
168
|
+
log.warn('argon2 not available, falling back to scrypt');
|
|
167
169
|
return this.hashWithScrypt(password);
|
|
168
170
|
}
|
|
169
171
|
throw error;
|
package/dist/jwt.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @module auth/jwt
|
|
4
4
|
*/
|
|
5
5
|
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
6
|
+
import { createLogger } from '@veloxts/core';
|
|
6
7
|
import { AuthError } from './types.js';
|
|
8
|
+
const log = createLogger('auth');
|
|
7
9
|
// ============================================================================
|
|
8
10
|
// Constants
|
|
9
11
|
// ============================================================================
|
|
@@ -161,11 +163,11 @@ export function validateTokenExpiration(accessExpiry, refreshExpiry) {
|
|
|
161
163
|
}
|
|
162
164
|
// Warn about exceeding recommended limits (non-fatal)
|
|
163
165
|
if (accessSeconds > TOKEN_BOUNDS.access.recommended) {
|
|
164
|
-
|
|
166
|
+
log.warn(`[Security] Access token expiry (${accessExpiry}) exceeds recommended maximum of 15 minutes. ` +
|
|
165
167
|
'Consider using shorter-lived access tokens with refresh.');
|
|
166
168
|
}
|
|
167
169
|
if (refreshSeconds > TOKEN_BOUNDS.refresh.recommended) {
|
|
168
|
-
|
|
170
|
+
log.warn(`[Security] Refresh token expiry (${refreshExpiry}) exceeds recommended maximum of 7 days. ` +
|
|
169
171
|
'Long-lived refresh tokens increase the window for token theft attacks.');
|
|
170
172
|
}
|
|
171
173
|
// Ensure refresh tokens outlive access tokens
|
package/dist/middleware.js
CHANGED
|
@@ -6,6 +6,16 @@ import { executeGuards } from './guards.js';
|
|
|
6
6
|
import { JwtManager } from './jwt.js';
|
|
7
7
|
import { AuthError } from './types.js';
|
|
8
8
|
// ============================================================================
|
|
9
|
+
// Constants
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const UNAUTHENTICATED_CONTEXT = {
|
|
12
|
+
authMode: 'native',
|
|
13
|
+
user: undefined,
|
|
14
|
+
token: undefined,
|
|
15
|
+
payload: undefined,
|
|
16
|
+
isAuthenticated: false,
|
|
17
|
+
};
|
|
18
|
+
// ============================================================================
|
|
9
19
|
// Auth Middleware Factory
|
|
10
20
|
// ============================================================================
|
|
11
21
|
/**
|
|
@@ -58,23 +68,10 @@ export function authMiddleware(config) {
|
|
|
58
68
|
// No token handling
|
|
59
69
|
if (!token) {
|
|
60
70
|
if (options.optional) {
|
|
61
|
-
// Optional auth - continue without user
|
|
62
|
-
const authContext = {
|
|
63
|
-
authMode: 'native',
|
|
64
|
-
user: undefined,
|
|
65
|
-
token: undefined,
|
|
66
|
-
payload: undefined,
|
|
67
|
-
isAuthenticated: false,
|
|
68
|
-
};
|
|
69
71
|
return next({
|
|
70
|
-
ctx: {
|
|
71
|
-
...ctx,
|
|
72
|
-
auth: authContext,
|
|
73
|
-
user: undefined,
|
|
74
|
-
},
|
|
72
|
+
ctx: { ...ctx, auth: UNAUTHENTICATED_CONTEXT, user: undefined },
|
|
75
73
|
});
|
|
76
74
|
}
|
|
77
|
-
// Required auth - reject
|
|
78
75
|
throw new AuthError('Authorization header required', 401);
|
|
79
76
|
}
|
|
80
77
|
// Verify token
|
|
@@ -84,20 +81,8 @@ export function authMiddleware(config) {
|
|
|
84
81
|
}
|
|
85
82
|
catch (error) {
|
|
86
83
|
if (options.optional) {
|
|
87
|
-
// Invalid token with optional auth - continue without user
|
|
88
|
-
const authContext = {
|
|
89
|
-
authMode: 'native',
|
|
90
|
-
user: undefined,
|
|
91
|
-
token: undefined,
|
|
92
|
-
payload: undefined,
|
|
93
|
-
isAuthenticated: false,
|
|
94
|
-
};
|
|
95
84
|
return next({
|
|
96
|
-
ctx: {
|
|
97
|
-
...ctx,
|
|
98
|
-
auth: authContext,
|
|
99
|
-
user: undefined,
|
|
100
|
-
},
|
|
85
|
+
ctx: { ...ctx, auth: UNAUTHENTICATED_CONTEXT, user: undefined },
|
|
101
86
|
});
|
|
102
87
|
}
|
|
103
88
|
throw new AuthError(error instanceof Error ? error.message : 'Invalid token', 401);
|
|
@@ -179,10 +164,6 @@ export function authMiddleware(config) {
|
|
|
179
164
|
};
|
|
180
165
|
}
|
|
181
166
|
// ============================================================================
|
|
182
|
-
// Error Helpers
|
|
183
|
-
// ============================================================================
|
|
184
|
-
// AuthError is now imported from types.ts
|
|
185
|
-
// ============================================================================
|
|
186
167
|
// Rate Limiting Middleware
|
|
187
168
|
// ============================================================================
|
|
188
169
|
/**
|
package/dist/password-policy.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* @module auth/password-policy
|
|
8
8
|
*/
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
|
+
import { createLogger } from '@veloxts/core';
|
|
11
|
+
const log = createLogger('auth');
|
|
10
12
|
/**
|
|
11
13
|
* Password strength levels
|
|
12
14
|
*/
|
|
@@ -253,7 +255,7 @@ export class PasswordPolicy {
|
|
|
253
255
|
}
|
|
254
256
|
catch (error) {
|
|
255
257
|
// Breach check failed - log but don't fail validation
|
|
256
|
-
|
|
258
|
+
log.warn('Password breach check failed:', error);
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
261
|
return {
|
package/dist/plugin.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { JwtAdapterConfig } from './adapters/jwt-adapter.js';
|
|
|
13
13
|
import { PasswordHasher } from './hash.js';
|
|
14
14
|
import type { JwtManager, TokenStore } from './jwt.js';
|
|
15
15
|
import { authMiddleware } from './middleware.js';
|
|
16
|
-
import type { AdapterAuthContext, AuthConfig, JwtConfig, TokenPair, User } from './types.js';
|
|
16
|
+
import type { AdapterAuthContext, AuthConfig, AuthContext, JwtConfig, TokenPair, User } from './types.js';
|
|
17
17
|
/** Auth package version */
|
|
18
18
|
export declare const AUTH_VERSION: string;
|
|
19
19
|
/**
|
|
@@ -60,7 +60,6 @@ export interface AuthService {
|
|
|
60
60
|
*/
|
|
61
61
|
middleware: ReturnType<typeof authMiddleware>;
|
|
62
62
|
}
|
|
63
|
-
import type { AuthContext } from './types.js';
|
|
64
63
|
declare module 'fastify' {
|
|
65
64
|
interface FastifyInstance {
|
|
66
65
|
auth: AuthService;
|
package/dist/policies.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Resource-level authorization policies for @veloxts/auth
|
|
3
3
|
* @module auth/policies
|
|
4
4
|
*/
|
|
5
|
+
import { createLogger } from '@veloxts/core';
|
|
6
|
+
const log = createLogger('auth');
|
|
5
7
|
// ============================================================================
|
|
6
8
|
// Policy Registry
|
|
7
9
|
// ============================================================================
|
|
@@ -82,7 +84,7 @@ export async function can(user, action, resourceName, resource) {
|
|
|
82
84
|
const policy = policyRegistry.get(resourceName);
|
|
83
85
|
if (!policy) {
|
|
84
86
|
// No policy registered = deny by default
|
|
85
|
-
|
|
87
|
+
log.warn(`No policy registered for resource: ${resourceName}`);
|
|
86
88
|
return false;
|
|
87
89
|
}
|
|
88
90
|
const actionHandler = policy[action];
|
package/dist/rate-limit.js
CHANGED
|
@@ -259,13 +259,9 @@ export function createAuthRateLimiter(config = {}) {
|
|
|
259
259
|
isLockedOut: (key, operation) => {
|
|
260
260
|
const fullKey = `auth:${operation}:${key}`;
|
|
261
261
|
const entry = authRateLimitStore.get(fullKey);
|
|
262
|
-
if (!entry
|
|
262
|
+
if (!entry?.lockoutUntil)
|
|
263
263
|
return false;
|
|
264
|
-
|
|
265
|
-
// Lockout expired
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
return true;
|
|
264
|
+
return entry.lockoutUntil > Date.now();
|
|
269
265
|
},
|
|
270
266
|
/**
|
|
271
267
|
* Get remaining attempts for a key
|
package/dist/session.js
CHANGED
|
@@ -214,8 +214,7 @@ export function sessionManager(config) {
|
|
|
214
214
|
modified = true;
|
|
215
215
|
},
|
|
216
216
|
getFlash(key) {
|
|
217
|
-
|
|
218
|
-
return value;
|
|
217
|
+
return currentData._flashOld?.[key];
|
|
219
218
|
},
|
|
220
219
|
getAllFlash() {
|
|
221
220
|
return currentData._flashOld ?? {};
|
|
@@ -400,6 +399,33 @@ export function sessionManager(config) {
|
|
|
400
399
|
};
|
|
401
400
|
}
|
|
402
401
|
// ============================================================================
|
|
402
|
+
// Session Helpers
|
|
403
|
+
// ============================================================================
|
|
404
|
+
/**
|
|
405
|
+
* Runs a middleware next() call with automatic session save on both success and error.
|
|
406
|
+
* Extracts the duplicated try/catch pattern from session middleware functions.
|
|
407
|
+
*/
|
|
408
|
+
async function withSessionSave(session, fn) {
|
|
409
|
+
try {
|
|
410
|
+
const result = await fn();
|
|
411
|
+
if (session.isModified && !session.isDestroyed) {
|
|
412
|
+
await session.save();
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
if (session.isModified && !session.isDestroyed) {
|
|
418
|
+
try {
|
|
419
|
+
await session.save();
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
// Ignore save errors during error handling
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// ============================================================================
|
|
403
429
|
// Session Middleware Factory
|
|
404
430
|
// ============================================================================
|
|
405
431
|
/**
|
|
@@ -465,31 +491,7 @@ export function sessionMiddleware(config) {
|
|
|
465
491
|
}
|
|
466
492
|
// Attach to request for hooks
|
|
467
493
|
request.session = session;
|
|
468
|
-
|
|
469
|
-
const result = await next({
|
|
470
|
-
ctx: {
|
|
471
|
-
...ctx,
|
|
472
|
-
session,
|
|
473
|
-
},
|
|
474
|
-
});
|
|
475
|
-
// Auto-save session if modified
|
|
476
|
-
if (session.isModified && !session.isDestroyed) {
|
|
477
|
-
await session.save();
|
|
478
|
-
}
|
|
479
|
-
return result;
|
|
480
|
-
}
|
|
481
|
-
catch (error) {
|
|
482
|
-
// Still try to save session on error
|
|
483
|
-
if (session.isModified && !session.isDestroyed) {
|
|
484
|
-
try {
|
|
485
|
-
await session.save();
|
|
486
|
-
}
|
|
487
|
-
catch {
|
|
488
|
-
// Ignore save errors during error handling
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
throw error;
|
|
492
|
-
}
|
|
494
|
+
return withSessionSave(session, () => next({ ctx: { ...ctx, session } }));
|
|
493
495
|
};
|
|
494
496
|
}
|
|
495
497
|
/**
|
|
@@ -526,31 +528,7 @@ export function sessionMiddleware(config) {
|
|
|
526
528
|
};
|
|
527
529
|
}
|
|
528
530
|
request.session = session;
|
|
529
|
-
|
|
530
|
-
const result = await next({
|
|
531
|
-
ctx: {
|
|
532
|
-
...ctx,
|
|
533
|
-
session,
|
|
534
|
-
user,
|
|
535
|
-
isAuthenticated: true,
|
|
536
|
-
},
|
|
537
|
-
});
|
|
538
|
-
if (session.isModified && !session.isDestroyed) {
|
|
539
|
-
await session.save();
|
|
540
|
-
}
|
|
541
|
-
return result;
|
|
542
|
-
}
|
|
543
|
-
catch (error) {
|
|
544
|
-
if (session.isModified && !session.isDestroyed) {
|
|
545
|
-
try {
|
|
546
|
-
await session.save();
|
|
547
|
-
}
|
|
548
|
-
catch {
|
|
549
|
-
// Ignore
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
throw error;
|
|
553
|
-
}
|
|
531
|
+
return withSessionSave(session, () => next({ ctx: { ...ctx, session, user, isAuthenticated: true } }));
|
|
554
532
|
};
|
|
555
533
|
}
|
|
556
534
|
/**
|
|
@@ -583,31 +561,7 @@ export function sessionMiddleware(config) {
|
|
|
583
561
|
}
|
|
584
562
|
}
|
|
585
563
|
request.session = session;
|
|
586
|
-
|
|
587
|
-
const result = await next({
|
|
588
|
-
ctx: {
|
|
589
|
-
...ctx,
|
|
590
|
-
session,
|
|
591
|
-
user,
|
|
592
|
-
isAuthenticated,
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
if (session.isModified && !session.isDestroyed) {
|
|
596
|
-
await session.save();
|
|
597
|
-
}
|
|
598
|
-
return result;
|
|
599
|
-
}
|
|
600
|
-
catch (error) {
|
|
601
|
-
if (session.isModified && !session.isDestroyed) {
|
|
602
|
-
try {
|
|
603
|
-
await session.save();
|
|
604
|
-
}
|
|
605
|
-
catch {
|
|
606
|
-
// Ignore
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
throw error;
|
|
610
|
-
}
|
|
564
|
+
return withSessionSave(session, () => next({ ctx: { ...ctx, session, user, isAuthenticated } }));
|
|
611
565
|
};
|
|
612
566
|
}
|
|
613
567
|
return {
|
package/dist/token-store.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module auth/token-store
|
|
10
10
|
*/
|
|
11
|
+
import { createLogger } from '@veloxts/core';
|
|
12
|
+
const log = createLogger('auth');
|
|
11
13
|
// ============================================================================
|
|
12
14
|
// Implementation
|
|
13
15
|
// ============================================================================
|
|
@@ -89,7 +91,7 @@ export function createEnhancedTokenStore(options) {
|
|
|
89
91
|
},
|
|
90
92
|
revokeAllUserTokens(userId) {
|
|
91
93
|
// Placeholder - in production, implement proper user->token mapping
|
|
92
|
-
|
|
94
|
+
log.warn(`[Security] Token reuse detected for user ${userId}. ` +
|
|
93
95
|
'All tokens should be revoked. Implement proper user->token mapping for production.');
|
|
94
96
|
},
|
|
95
97
|
clear() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/auth",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Authentication and authorization system for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@fastify/cookie": "11.0.2",
|
|
63
63
|
"fastify": "5.7.4",
|
|
64
|
-
"@veloxts/core": "0.7.
|
|
65
|
-
"@veloxts/router": "0.7.
|
|
64
|
+
"@veloxts/core": "0.7.2",
|
|
65
|
+
"@veloxts/router": "0.7.2"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"argon2": ">=0.30.0",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"@vitest/coverage-v8": "4.0.18",
|
|
86
86
|
"typescript": "5.9.3",
|
|
87
87
|
"vitest": "4.0.18",
|
|
88
|
-
"@veloxts/testing": "0.7.
|
|
89
|
-
"@veloxts/validation": "0.7.
|
|
88
|
+
"@veloxts/testing": "0.7.2",
|
|
89
|
+
"@veloxts/validation": "0.7.2"
|
|
90
90
|
},
|
|
91
91
|
"keywords": [
|
|
92
92
|
"velox",
|