@noony-serverless/core 0.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/README.md +443 -0
- package/build/core/containerPool.d.ts +44 -0
- package/build/core/containerPool.js +103 -0
- package/build/core/core.d.ts +123 -0
- package/build/core/core.js +107 -0
- package/build/core/errors.d.ts +25 -0
- package/build/core/errors.js +59 -0
- package/build/core/handler.d.ts +72 -0
- package/build/core/handler.js +151 -0
- package/build/core/index.d.ts +8 -0
- package/build/core/index.js +24 -0
- package/build/core/logger.d.ts +42 -0
- package/build/core/logger.js +135 -0
- package/build/core/performanceMonitor.d.ts +73 -0
- package/build/core/performanceMonitor.js +189 -0
- package/build/index.d.ts +3 -0
- package/build/index.js +19 -0
- package/build/middlewares/authenticationMiddleware.d.ts +52 -0
- package/build/middlewares/authenticationMiddleware.js +204 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +31 -0
- package/build/middlewares/bodyParserMiddleware.js +217 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +12 -0
- package/build/middlewares/bodyValidationMiddleware.js +34 -0
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +14 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +48 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +6 -0
- package/build/middlewares/errorHandlerMiddleware.js +64 -0
- package/build/middlewares/headerVariablesMiddleware.d.ts +8 -0
- package/build/middlewares/headerVariablesMiddleware.js +32 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +10 -0
- package/build/middlewares/httpAttributesMiddleware.js +71 -0
- package/build/middlewares/index.d.ts +14 -0
- package/build/middlewares/index.js +30 -0
- package/build/middlewares/queryParametersMiddleware.d.ts +8 -0
- package/build/middlewares/queryParametersMiddleware.js +51 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +157 -0
- package/build/middlewares/rateLimitingMiddleware.js +237 -0
- package/build/middlewares/responseWrapperMiddleware.d.ts +11 -0
- package/build/middlewares/responseWrapperMiddleware.js +34 -0
- package/build/middlewares/securityAuditMiddleware.d.ts +124 -0
- package/build/middlewares/securityAuditMiddleware.js +395 -0
- package/build/middlewares/securityHeadersMiddleware.d.ts +128 -0
- package/build/middlewares/securityHeadersMiddleware.js +183 -0
- package/build/middlewares/validationMiddleware.d.ts +9 -0
- package/build/middlewares/validationMiddleware.js +40 -0
- package/package.json +73 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance monitoring utilities for production use
|
|
3
|
+
* Provides lightweight performance tracking without impacting application performance
|
|
4
|
+
*/
|
|
5
|
+
interface AggregatedMetrics {
|
|
6
|
+
count: number;
|
|
7
|
+
totalDuration: number;
|
|
8
|
+
averageDuration: number;
|
|
9
|
+
minDuration: number;
|
|
10
|
+
maxDuration: number;
|
|
11
|
+
p95Duration: number;
|
|
12
|
+
}
|
|
13
|
+
declare class PerformanceMonitor {
|
|
14
|
+
private metrics;
|
|
15
|
+
private readonly maxMetricsPerOperation;
|
|
16
|
+
private isEnabled;
|
|
17
|
+
constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Start timing an operation
|
|
20
|
+
*/
|
|
21
|
+
startTiming(operationName: string): () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Time an async operation
|
|
24
|
+
*/
|
|
25
|
+
timeAsync<T>(operationName: string, operation: () => Promise<T>): Promise<T>;
|
|
26
|
+
/**
|
|
27
|
+
* Time a synchronous operation
|
|
28
|
+
*/
|
|
29
|
+
timeSync<T>(operationName: string, operation: () => T): T;
|
|
30
|
+
/**
|
|
31
|
+
* Record a metric manually
|
|
32
|
+
*/
|
|
33
|
+
recordMetric(operationName: string, duration: number, metadata?: Record<string, unknown>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Get aggregated metrics for an operation
|
|
36
|
+
*/
|
|
37
|
+
getMetrics(operationName: string): AggregatedMetrics | null;
|
|
38
|
+
/**
|
|
39
|
+
* Get all metrics summary
|
|
40
|
+
*/
|
|
41
|
+
getAllMetrics(): Record<string, AggregatedMetrics>;
|
|
42
|
+
/**
|
|
43
|
+
* Reset all metrics
|
|
44
|
+
*/
|
|
45
|
+
reset(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Get performance summary for health checks
|
|
48
|
+
*/
|
|
49
|
+
getHealthSummary(): {
|
|
50
|
+
isEnabled: boolean;
|
|
51
|
+
trackedOperations: number;
|
|
52
|
+
totalMetrics: number;
|
|
53
|
+
slowOperations: Array<{
|
|
54
|
+
name: string;
|
|
55
|
+
avgDuration: number;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Enable or disable monitoring
|
|
60
|
+
*/
|
|
61
|
+
setEnabled(enabled: boolean): void;
|
|
62
|
+
}
|
|
63
|
+
export declare const performanceMonitor: PerformanceMonitor;
|
|
64
|
+
/**
|
|
65
|
+
* Decorator for timing method calls
|
|
66
|
+
*/
|
|
67
|
+
export declare function timed(operationName?: string): MethodDecorator;
|
|
68
|
+
/**
|
|
69
|
+
* Decorator for timing synchronous method calls
|
|
70
|
+
*/
|
|
71
|
+
export declare function timedSync(operationName?: string): MethodDecorator;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=performanceMonitor.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Performance monitoring utilities for production use
|
|
4
|
+
* Provides lightweight performance tracking without impacting application performance
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.performanceMonitor = void 0;
|
|
8
|
+
exports.timed = timed;
|
|
9
|
+
exports.timedSync = timedSync;
|
|
10
|
+
const logger_1 = require("./logger");
|
|
11
|
+
class PerformanceMonitor {
|
|
12
|
+
metrics = new Map();
|
|
13
|
+
maxMetricsPerOperation = 1000; // Limit memory usage
|
|
14
|
+
isEnabled;
|
|
15
|
+
constructor() {
|
|
16
|
+
// Enable performance monitoring based on environment
|
|
17
|
+
this.isEnabled =
|
|
18
|
+
process.env.NODE_ENV === 'development' ||
|
|
19
|
+
process.env.PERFORMANCE_MONITORING === 'true';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start timing an operation
|
|
23
|
+
*/
|
|
24
|
+
startTiming(operationName) {
|
|
25
|
+
if (!this.isEnabled) {
|
|
26
|
+
return () => { }; // No-op function for production
|
|
27
|
+
}
|
|
28
|
+
const startTime = process.hrtime.bigint();
|
|
29
|
+
return () => {
|
|
30
|
+
const endTime = process.hrtime.bigint();
|
|
31
|
+
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
|
|
32
|
+
this.recordMetric(operationName, duration);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Time an async operation
|
|
37
|
+
*/
|
|
38
|
+
async timeAsync(operationName, operation) {
|
|
39
|
+
if (!this.isEnabled) {
|
|
40
|
+
return operation();
|
|
41
|
+
}
|
|
42
|
+
const stopTiming = this.startTiming(operationName);
|
|
43
|
+
try {
|
|
44
|
+
const result = await operation();
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
stopTiming();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Time a synchronous operation
|
|
53
|
+
*/
|
|
54
|
+
timeSync(operationName, operation) {
|
|
55
|
+
if (!this.isEnabled) {
|
|
56
|
+
return operation();
|
|
57
|
+
}
|
|
58
|
+
const stopTiming = this.startTiming(operationName);
|
|
59
|
+
try {
|
|
60
|
+
const result = operation();
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
stopTiming();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Record a metric manually
|
|
69
|
+
*/
|
|
70
|
+
recordMetric(operationName, duration, metadata) {
|
|
71
|
+
if (!this.isEnabled)
|
|
72
|
+
return;
|
|
73
|
+
// Get or create metrics array for this operation
|
|
74
|
+
let operationMetrics = this.metrics.get(operationName);
|
|
75
|
+
if (!operationMetrics) {
|
|
76
|
+
operationMetrics = [];
|
|
77
|
+
this.metrics.set(operationName, operationMetrics);
|
|
78
|
+
}
|
|
79
|
+
// Add the metric
|
|
80
|
+
operationMetrics.push(duration);
|
|
81
|
+
// Maintain size limit to prevent memory leaks
|
|
82
|
+
if (operationMetrics.length > this.maxMetricsPerOperation) {
|
|
83
|
+
operationMetrics.shift(); // Remove oldest metric
|
|
84
|
+
}
|
|
85
|
+
// Log slow operations
|
|
86
|
+
if (duration > 100) {
|
|
87
|
+
// Log operations over 100ms
|
|
88
|
+
logger_1.logger.logPerformance(operationName, duration, metadata);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get aggregated metrics for an operation
|
|
93
|
+
*/
|
|
94
|
+
getMetrics(operationName) {
|
|
95
|
+
const metrics = this.metrics.get(operationName);
|
|
96
|
+
if (!metrics || metrics.length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const sortedMetrics = [...metrics].sort((a, b) => a - b);
|
|
100
|
+
const count = metrics.length;
|
|
101
|
+
const totalDuration = metrics.reduce((sum, duration) => sum + duration, 0);
|
|
102
|
+
const p95Index = Math.ceil(count * 0.95) - 1;
|
|
103
|
+
return {
|
|
104
|
+
count,
|
|
105
|
+
totalDuration,
|
|
106
|
+
averageDuration: totalDuration / count,
|
|
107
|
+
minDuration: sortedMetrics[0],
|
|
108
|
+
maxDuration: sortedMetrics[count - 1],
|
|
109
|
+
p95Duration: sortedMetrics[p95Index],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get all metrics summary
|
|
114
|
+
*/
|
|
115
|
+
getAllMetrics() {
|
|
116
|
+
const summary = {};
|
|
117
|
+
for (const [operationName] of this.metrics) {
|
|
118
|
+
const metrics = this.getMetrics(operationName);
|
|
119
|
+
if (metrics) {
|
|
120
|
+
summary[operationName] = metrics;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return summary;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Reset all metrics
|
|
127
|
+
*/
|
|
128
|
+
reset() {
|
|
129
|
+
this.metrics.clear();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get performance summary for health checks
|
|
133
|
+
*/
|
|
134
|
+
getHealthSummary() {
|
|
135
|
+
const allMetrics = this.getAllMetrics();
|
|
136
|
+
const slowOperations = Object.entries(allMetrics)
|
|
137
|
+
.filter(([_, metrics]) => metrics.averageDuration > 50) // Operations over 50ms average
|
|
138
|
+
.map(([name, metrics]) => ({
|
|
139
|
+
name,
|
|
140
|
+
avgDuration: metrics.averageDuration,
|
|
141
|
+
}))
|
|
142
|
+
.sort((a, b) => b.avgDuration - a.avgDuration);
|
|
143
|
+
const totalMetrics = Array.from(this.metrics.values()).reduce((sum, metrics) => sum + metrics.length, 0);
|
|
144
|
+
return {
|
|
145
|
+
isEnabled: this.isEnabled,
|
|
146
|
+
trackedOperations: this.metrics.size,
|
|
147
|
+
totalMetrics,
|
|
148
|
+
slowOperations,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Enable or disable monitoring
|
|
153
|
+
*/
|
|
154
|
+
setEnabled(enabled) {
|
|
155
|
+
this.isEnabled = enabled;
|
|
156
|
+
if (!enabled) {
|
|
157
|
+
this.reset();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Global performance monitor instance
|
|
162
|
+
exports.performanceMonitor = new PerformanceMonitor();
|
|
163
|
+
/**
|
|
164
|
+
* Decorator for timing method calls
|
|
165
|
+
*/
|
|
166
|
+
function timed(operationName) {
|
|
167
|
+
return function (target, propertyKey, descriptor) {
|
|
168
|
+
const originalMethod = descriptor.value;
|
|
169
|
+
const name = operationName || `${target.constructor.name}.${String(propertyKey)}`;
|
|
170
|
+
descriptor.value = async function (...args) {
|
|
171
|
+
return exports.performanceMonitor.timeAsync(name, () => originalMethod.apply(this, args));
|
|
172
|
+
};
|
|
173
|
+
return descriptor;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Decorator for timing synchronous method calls
|
|
178
|
+
*/
|
|
179
|
+
function timedSync(operationName) {
|
|
180
|
+
return function (target, propertyKey, descriptor) {
|
|
181
|
+
const originalMethod = descriptor.value;
|
|
182
|
+
const name = operationName || `${target.constructor.name}.${String(propertyKey)}`;
|
|
183
|
+
descriptor.value = function (...args) {
|
|
184
|
+
return exports.performanceMonitor.timeSync(name, () => originalMethod.apply(this, args));
|
|
185
|
+
};
|
|
186
|
+
return descriptor;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=performanceMonitor.js.map
|
package/build/index.d.ts
ADDED
package/build/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./core"), exports);
|
|
18
|
+
__exportStar(require("./middlewares"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BaseMiddleware } from '../core/handler';
|
|
2
|
+
import { Context } from '../core/core';
|
|
3
|
+
export interface CustomTokenVerificationPort<T> {
|
|
4
|
+
verifyToken(token: string): Promise<T>;
|
|
5
|
+
}
|
|
6
|
+
export interface JWTPayload {
|
|
7
|
+
exp?: number;
|
|
8
|
+
iat?: number;
|
|
9
|
+
nbf?: number;
|
|
10
|
+
jti?: string;
|
|
11
|
+
iss?: string;
|
|
12
|
+
aud?: string | string[];
|
|
13
|
+
sub?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface AuthenticationOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Maximum token age in seconds (overrides exp claim validation)
|
|
19
|
+
*/
|
|
20
|
+
maxTokenAge?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Clock tolerance in seconds for time-based validations
|
|
23
|
+
* @default 60
|
|
24
|
+
*/
|
|
25
|
+
clockTolerance?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Token blacklist checker function
|
|
28
|
+
*/
|
|
29
|
+
isTokenBlacklisted?: (tokenId?: string) => Promise<boolean> | boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Rate limiting per user/IP
|
|
32
|
+
*/
|
|
33
|
+
rateLimiting?: {
|
|
34
|
+
maxAttempts: number;
|
|
35
|
+
windowMs: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Required token claims
|
|
39
|
+
*/
|
|
40
|
+
requiredClaims?: {
|
|
41
|
+
issuer?: string;
|
|
42
|
+
audience?: string | string[];
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare class AuthenticationMiddleware<T> implements BaseMiddleware {
|
|
46
|
+
private tokenVerificationPort;
|
|
47
|
+
private options;
|
|
48
|
+
constructor(tokenVerificationPort: CustomTokenVerificationPort<T>, options?: AuthenticationOptions);
|
|
49
|
+
before(context: Context): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export declare const verifyAuthTokenMiddleware: <T>(tokenVerificationPort: CustomTokenVerificationPort<T>, options?: AuthenticationOptions) => BaseMiddleware;
|
|
52
|
+
//# sourceMappingURL=authenticationMiddleware.d.ts.map
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyAuthTokenMiddleware = exports.AuthenticationMiddleware = void 0;
|
|
4
|
+
const errors_1 = require("../core/errors");
|
|
5
|
+
const logger_1 = require("../core/logger");
|
|
6
|
+
// Simple in-memory store for rate limiting (use Redis in production)
|
|
7
|
+
const rateLimitStore = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Enhanced JWT validation with comprehensive security checks
|
|
10
|
+
*/
|
|
11
|
+
const validateJWTSecurity = (payload, options = {}, clientIP) => {
|
|
12
|
+
const now = Math.floor(Date.now() / 1000);
|
|
13
|
+
const clockTolerance = options.clockTolerance || 60;
|
|
14
|
+
// Validate expiration time
|
|
15
|
+
if (payload.exp !== undefined) {
|
|
16
|
+
if (payload.exp <= now - clockTolerance) {
|
|
17
|
+
logger_1.logger.warn('Token expired', {
|
|
18
|
+
expiredAt: new Date(payload.exp * 1000).toISOString(),
|
|
19
|
+
currentTime: new Date(now * 1000).toISOString(),
|
|
20
|
+
clientIP,
|
|
21
|
+
});
|
|
22
|
+
throw new errors_1.AuthenticationError('Token expired');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Validate not-before time
|
|
26
|
+
if (payload.nbf !== undefined) {
|
|
27
|
+
if (payload.nbf > now + clockTolerance) {
|
|
28
|
+
logger_1.logger.warn('Token used before valid time', {
|
|
29
|
+
notBefore: new Date(payload.nbf * 1000).toISOString(),
|
|
30
|
+
currentTime: new Date(now * 1000).toISOString(),
|
|
31
|
+
clientIP,
|
|
32
|
+
});
|
|
33
|
+
throw new errors_1.AuthenticationError('Token not yet valid');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Validate issued-at time if maxTokenAge is specified
|
|
37
|
+
if (options.maxTokenAge && payload.iat !== undefined) {
|
|
38
|
+
const tokenAge = now - payload.iat;
|
|
39
|
+
if (tokenAge > options.maxTokenAge) {
|
|
40
|
+
logger_1.logger.warn('Token too old', {
|
|
41
|
+
tokenAge: `${tokenAge}s`,
|
|
42
|
+
maxAge: `${options.maxTokenAge}s`,
|
|
43
|
+
clientIP,
|
|
44
|
+
});
|
|
45
|
+
throw new errors_1.AuthenticationError('Token too old');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Validate required claims
|
|
49
|
+
if (options.requiredClaims) {
|
|
50
|
+
if (options.requiredClaims.issuer &&
|
|
51
|
+
payload.iss !== options.requiredClaims.issuer) {
|
|
52
|
+
logger_1.logger.warn('Invalid token issuer', {
|
|
53
|
+
expected: options.requiredClaims.issuer,
|
|
54
|
+
received: payload.iss,
|
|
55
|
+
clientIP,
|
|
56
|
+
});
|
|
57
|
+
throw new errors_1.SecurityError('Invalid token issuer');
|
|
58
|
+
}
|
|
59
|
+
if (options.requiredClaims.audience) {
|
|
60
|
+
const requiredAud = options.requiredClaims.audience;
|
|
61
|
+
const tokenAud = payload.aud;
|
|
62
|
+
let isValidAudience = false;
|
|
63
|
+
if (Array.isArray(requiredAud)) {
|
|
64
|
+
isValidAudience = Array.isArray(tokenAud)
|
|
65
|
+
? tokenAud.some((aud) => requiredAud.includes(aud))
|
|
66
|
+
: requiredAud.includes(tokenAud || '');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
isValidAudience = Array.isArray(tokenAud)
|
|
70
|
+
? tokenAud.includes(requiredAud)
|
|
71
|
+
: tokenAud === requiredAud;
|
|
72
|
+
}
|
|
73
|
+
if (!isValidAudience) {
|
|
74
|
+
logger_1.logger.warn('Invalid token audience', {
|
|
75
|
+
expected: requiredAud,
|
|
76
|
+
received: tokenAud,
|
|
77
|
+
clientIP,
|
|
78
|
+
});
|
|
79
|
+
throw new errors_1.SecurityError('Invalid token audience');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Check rate limiting for authentication attempts
|
|
86
|
+
*/
|
|
87
|
+
const checkRateLimit = (identifier, options) => {
|
|
88
|
+
if (!options.rateLimiting)
|
|
89
|
+
return;
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
const key = `auth:${identifier}`;
|
|
92
|
+
const limit = rateLimitStore.get(key);
|
|
93
|
+
if (limit && now < limit.resetTime) {
|
|
94
|
+
if (limit.attempts >= options.rateLimiting.maxAttempts) {
|
|
95
|
+
logger_1.logger.warn('Rate limit exceeded for authentication', {
|
|
96
|
+
identifier,
|
|
97
|
+
attempts: limit.attempts,
|
|
98
|
+
resetTime: new Date(limit.resetTime).toISOString(),
|
|
99
|
+
});
|
|
100
|
+
throw new errors_1.SecurityError('Too many authentication attempts. Please try again later.');
|
|
101
|
+
}
|
|
102
|
+
limit.attempts++;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
rateLimitStore.set(key, {
|
|
106
|
+
attempts: 1,
|
|
107
|
+
resetTime: now + options.rateLimiting.windowMs,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
async function verifyToken(tokenVerificationPort, context, options = {}) {
|
|
112
|
+
const authHeader = context.req.headers?.authorization;
|
|
113
|
+
const clientIP = context.req.ip ||
|
|
114
|
+
context.req.headers?.['x-forwarded-for'] ||
|
|
115
|
+
'unknown';
|
|
116
|
+
const userAgent = context.req.headers?.['user-agent'];
|
|
117
|
+
if (!authHeader) {
|
|
118
|
+
logger_1.logger.warn('Missing authorization header', { clientIP, userAgent });
|
|
119
|
+
throw new errors_1.HttpError(401, 'No authorization header');
|
|
120
|
+
}
|
|
121
|
+
const authHeaderString = Array.isArray(authHeader)
|
|
122
|
+
? authHeader[0]
|
|
123
|
+
: authHeader;
|
|
124
|
+
const token = authHeaderString?.split('Bearer ')[1];
|
|
125
|
+
if (!token) {
|
|
126
|
+
logger_1.logger.warn('Invalid token format', { clientIP, userAgent });
|
|
127
|
+
throw new errors_1.AuthenticationError('Invalid token format');
|
|
128
|
+
}
|
|
129
|
+
// Check rate limiting
|
|
130
|
+
checkRateLimit(clientIP, options);
|
|
131
|
+
try {
|
|
132
|
+
// Verify token through port
|
|
133
|
+
const user = await tokenVerificationPort.verifyToken(token);
|
|
134
|
+
// If user has JWT payload, validate security aspects
|
|
135
|
+
if (user && typeof user === 'object' && 'exp' in user) {
|
|
136
|
+
validateJWTSecurity(user, options, clientIP);
|
|
137
|
+
// Check token blacklist if configured
|
|
138
|
+
if (options.isTokenBlacklisted && 'jti' in user) {
|
|
139
|
+
const isBlacklisted = await options.isTokenBlacklisted(user.jti);
|
|
140
|
+
if (isBlacklisted) {
|
|
141
|
+
logger_1.logger.warn('Blacklisted token used', {
|
|
142
|
+
tokenId: user.jti,
|
|
143
|
+
clientIP,
|
|
144
|
+
userAgent,
|
|
145
|
+
});
|
|
146
|
+
throw new errors_1.SecurityError('Token has been revoked');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
context.user = user;
|
|
151
|
+
logger_1.logger.debug('Successful authentication', {
|
|
152
|
+
userId: typeof user === 'object' && user && 'sub' in user
|
|
153
|
+
? String(user.sub)
|
|
154
|
+
: 'unknown',
|
|
155
|
+
clientIP,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
// Log failed authentication attempt
|
|
160
|
+
logger_1.logger.warn('Authentication failed', {
|
|
161
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
162
|
+
clientIP,
|
|
163
|
+
userAgent,
|
|
164
|
+
tokenPreview: token.substring(0, 10) + '...',
|
|
165
|
+
});
|
|
166
|
+
if (error instanceof errors_1.HttpError) {
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
throw new errors_1.AuthenticationError('Invalid authentication');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
class AuthenticationMiddleware {
|
|
173
|
+
tokenVerificationPort;
|
|
174
|
+
options;
|
|
175
|
+
constructor(tokenVerificationPort, options = {}) {
|
|
176
|
+
this.tokenVerificationPort = tokenVerificationPort;
|
|
177
|
+
this.options = options;
|
|
178
|
+
}
|
|
179
|
+
async before(context) {
|
|
180
|
+
await verifyToken(this.tokenVerificationPort, context, this.options);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.AuthenticationMiddleware = AuthenticationMiddleware;
|
|
184
|
+
const verifyAuthTokenMiddleware = (tokenVerificationPort, options = {}) => ({
|
|
185
|
+
async before(context) {
|
|
186
|
+
await verifyToken(tokenVerificationPort, context, options);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
exports.verifyAuthTokenMiddleware = verifyAuthTokenMiddleware;
|
|
190
|
+
/*
|
|
191
|
+
// Example protected endpoint
|
|
192
|
+
const protectedHandler = new Handler()
|
|
193
|
+
.use(verifyAuthTokenMiddleware(customTokenVerificationPort))
|
|
194
|
+
.use(errorHandler())
|
|
195
|
+
.use(responseWrapperMiddleware<any>())
|
|
196
|
+
.handle(async (context: Context) => {
|
|
197
|
+
const user = context.user;
|
|
198
|
+
setResponseData(context, {
|
|
199
|
+
message: 'Protected endpoint',
|
|
200
|
+
user,
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
*/
|
|
204
|
+
//# sourceMappingURL=authenticationMiddleware.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BaseMiddleware, Context } from '../core';
|
|
2
|
+
/**
|
|
3
|
+
* Enhanced BodyParserMiddleware with async parsing and performance optimizations.
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Async JSON parsing for large payloads
|
|
7
|
+
* - Size limits to prevent DoS attacks
|
|
8
|
+
* - Base64 decoding for Pub/Sub messages
|
|
9
|
+
* - Non-blocking parsing using setImmediate
|
|
10
|
+
*
|
|
11
|
+
* @template T - The expected type of the parsed body. Defaults to unknown if not specified.
|
|
12
|
+
* @implements {BaseMiddleware}
|
|
13
|
+
*/
|
|
14
|
+
export declare class BodyParserMiddleware<T = unknown> implements BaseMiddleware {
|
|
15
|
+
private maxSize;
|
|
16
|
+
constructor(maxSize?: number);
|
|
17
|
+
before(context: Context): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Enhanced middleware function for parsing the request body in specific HTTP methods.
|
|
21
|
+
*
|
|
22
|
+
* Performance optimizations:
|
|
23
|
+
* - Early method validation
|
|
24
|
+
* - Async parsing for large payloads
|
|
25
|
+
* - Size validation
|
|
26
|
+
*
|
|
27
|
+
* @template T - The expected type of the parsed request body.
|
|
28
|
+
* @returns {BaseMiddleware} A middleware object containing a `before` hook.
|
|
29
|
+
*/
|
|
30
|
+
export declare const bodyParser: <T = unknown>(maxSize?: number) => BaseMiddleware;
|
|
31
|
+
//# sourceMappingURL=bodyParserMiddleware.d.ts.map
|