@noony-serverless/core 0.1.1 → 0.1.5
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/build/middlewares/authenticationMiddleware.d.ts +379 -0
- package/build/middlewares/authenticationMiddleware.js +216 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +99 -0
- package/build/middlewares/bodyParserMiddleware.js +99 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +68 -4
- package/build/middlewares/bodyValidationMiddleware.js +64 -0
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +238 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +238 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +94 -0
- package/build/middlewares/errorHandlerMiddleware.js +105 -0
- package/build/middlewares/guards/RouteGuards.d.ts +239 -19
- package/build/middlewares/guards/RouteGuards.js +119 -15
- package/build/middlewares/guards/cache/CacheAdapter.d.ts +369 -28
- package/build/middlewares/guards/cache/CacheAdapter.js +124 -5
- package/build/middlewares/guards/cache/MemoryCacheAdapter.d.ts +113 -4
- package/build/middlewares/guards/cache/MemoryCacheAdapter.js +113 -4
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +518 -18
- package/build/middlewares/guards/config/GuardConfiguration.js +207 -10
- package/build/middlewares/guards/services/FastUserContextService.js +2 -1
- package/build/middlewares/headerVariablesMiddleware.d.ts +118 -0
- package/build/middlewares/headerVariablesMiddleware.js +118 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +235 -0
- package/build/middlewares/httpAttributesMiddleware.js +235 -0
- package/build/middlewares/queryParametersMiddleware.d.ts +105 -0
- package/build/middlewares/queryParametersMiddleware.js +105 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +109 -5
- package/build/middlewares/rateLimitingMiddleware.js +109 -5
- package/build/middlewares/responseWrapperMiddleware.d.ts +170 -1
- package/build/middlewares/responseWrapperMiddleware.js +170 -1
- package/build/middlewares/securityAuditMiddleware.js +5 -5
- package/build/middlewares/validationMiddleware.d.ts +145 -0
- package/build/middlewares/validationMiddleware.js +145 -0
- package/package.json +1 -1
|
@@ -1,8 +1,107 @@
|
|
|
1
1
|
import { BaseMiddleware } from '../core/handler';
|
|
2
2
|
import { Context } from '../core/core';
|
|
3
|
+
/**
|
|
4
|
+
* Interface for custom token verification implementations.
|
|
5
|
+
* Allows integration with various authentication providers (JWT, OAuth, custom tokens).
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of user data returned after successful token verification
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* JWT token verification:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import jwt from 'jsonwebtoken';
|
|
13
|
+
* import { CustomTokenVerificationPort } from '@noony-serverless/core';
|
|
14
|
+
*
|
|
15
|
+
* interface User {
|
|
16
|
+
* id: string;
|
|
17
|
+
* email: string;
|
|
18
|
+
* roles: string[];
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* class JWTVerificationPort implements CustomTokenVerificationPort<User> {
|
|
22
|
+
* constructor(private secret: string) {}
|
|
23
|
+
*
|
|
24
|
+
* async verifyToken(token: string): Promise<User> {
|
|
25
|
+
* try {
|
|
26
|
+
* const payload = jwt.verify(token, this.secret) as any;
|
|
27
|
+
* return {
|
|
28
|
+
* id: payload.sub,
|
|
29
|
+
* email: payload.email,
|
|
30
|
+
* roles: payload.roles || []
|
|
31
|
+
* };
|
|
32
|
+
* } catch (error) {
|
|
33
|
+
* throw new Error('Invalid token');
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* Custom API token verification:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* class APIKeyVerificationPort implements CustomTokenVerificationPort<{ apiKey: string; permissions: string[] }> {
|
|
43
|
+
* async verifyToken(token: string): Promise<{ apiKey: string; permissions: string[] }> {
|
|
44
|
+
* const apiKey = await this.validateAPIKey(token);
|
|
45
|
+
* if (!apiKey) {
|
|
46
|
+
* throw new Error('Invalid API key');
|
|
47
|
+
* }
|
|
48
|
+
* return {
|
|
49
|
+
* apiKey: token,
|
|
50
|
+
* permissions: apiKey.permissions
|
|
51
|
+
* };
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* private async validateAPIKey(key: string) {
|
|
55
|
+
* // Validate against database or external service
|
|
56
|
+
* return { permissions: ['read', 'write'] };
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
3
61
|
export interface CustomTokenVerificationPort<T> {
|
|
4
62
|
verifyToken(token: string): Promise<T>;
|
|
5
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Standard JWT payload interface with common claims.
|
|
66
|
+
* Extends the payload with custom properties as needed.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* Basic JWT payload usage:
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { JWTPayload } from '@noony-serverless/core';
|
|
72
|
+
*
|
|
73
|
+
* interface CustomJWTPayload extends JWTPayload {
|
|
74
|
+
* userId: string;
|
|
75
|
+
* email: string;
|
|
76
|
+
* roles: string[];
|
|
77
|
+
* }
|
|
78
|
+
*
|
|
79
|
+
* const payload: CustomJWTPayload = {
|
|
80
|
+
* sub: 'user-123',
|
|
81
|
+
* exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
|
|
82
|
+
* iat: Math.floor(Date.now() / 1000),
|
|
83
|
+
* iss: 'my-app',
|
|
84
|
+
* aud: 'my-app-users',
|
|
85
|
+
* userId: 'user-123',
|
|
86
|
+
* email: 'user@example.com',
|
|
87
|
+
* roles: ['user', 'admin']
|
|
88
|
+
* };
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* Token validation with custom claims:
|
|
93
|
+
* ```typescript
|
|
94
|
+
* function validateCustomClaims(payload: JWTPayload & { roles?: string[] }) {
|
|
95
|
+
* if (!payload.roles || payload.roles.length === 0) {
|
|
96
|
+
* throw new Error('User must have at least one role');
|
|
97
|
+
* }
|
|
98
|
+
*
|
|
99
|
+
* if (payload.exp && payload.exp < Date.now() / 1000) {
|
|
100
|
+
* throw new Error('Token has expired');
|
|
101
|
+
* }
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
6
105
|
export interface JWTPayload {
|
|
7
106
|
exp?: number;
|
|
8
107
|
iat?: number;
|
|
@@ -13,6 +112,70 @@ export interface JWTPayload {
|
|
|
13
112
|
sub?: string;
|
|
14
113
|
[key: string]: unknown;
|
|
15
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Configuration options for authentication middleware.
|
|
117
|
+
* Provides comprehensive security controls and validation settings.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* Basic authentication options:
|
|
121
|
+
* ```typescript
|
|
122
|
+
* import { AuthenticationOptions } from '@noony-serverless/core';
|
|
123
|
+
*
|
|
124
|
+
* const basicOptions: AuthenticationOptions = {
|
|
125
|
+
* maxTokenAge: 3600, // 1 hour
|
|
126
|
+
* clockTolerance: 60, // 1 minute
|
|
127
|
+
* requiredClaims: {
|
|
128
|
+
* issuer: 'my-app',
|
|
129
|
+
* audience: 'my-app-users'
|
|
130
|
+
* }
|
|
131
|
+
* };
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* Advanced options with rate limiting and blacklisting:
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const advancedOptions: AuthenticationOptions = {
|
|
138
|
+
* maxTokenAge: 7200, // 2 hours
|
|
139
|
+
* clockTolerance: 30,
|
|
140
|
+
* rateLimiting: {
|
|
141
|
+
* maxAttempts: 5,
|
|
142
|
+
* windowMs: 15 * 60 * 1000 // 15 minutes
|
|
143
|
+
* },
|
|
144
|
+
* isTokenBlacklisted: async (tokenId) => {
|
|
145
|
+
* // Check Redis or database for blacklisted tokens
|
|
146
|
+
* return await redis.sismember('blacklisted_tokens', tokenId);
|
|
147
|
+
* },
|
|
148
|
+
* requiredClaims: {
|
|
149
|
+
* issuer: 'secure-app',
|
|
150
|
+
* audience: ['web-app', 'mobile-app']
|
|
151
|
+
* }
|
|
152
|
+
* };
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* Production security configuration:
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const productionOptions: AuthenticationOptions = {
|
|
159
|
+
* maxTokenAge: 1800, // 30 minutes - short for security
|
|
160
|
+
* clockTolerance: 10, // Tight tolerance
|
|
161
|
+
* rateLimiting: {
|
|
162
|
+
* maxAttempts: 3, // Strict rate limiting
|
|
163
|
+
* windowMs: 30 * 60 * 1000 // 30 minutes lockout
|
|
164
|
+
* },
|
|
165
|
+
* isTokenBlacklisted: async (tokenId) => {
|
|
166
|
+
* const result = await database.query(
|
|
167
|
+
* 'SELECT 1 FROM revoked_tokens WHERE jti = ?',
|
|
168
|
+
* [tokenId]
|
|
169
|
+
* );
|
|
170
|
+
* return result.length > 0;
|
|
171
|
+
* },
|
|
172
|
+
* requiredClaims: {
|
|
173
|
+
* issuer: 'production-auth-server',
|
|
174
|
+
* audience: 'production-api'
|
|
175
|
+
* }
|
|
176
|
+
* };
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
16
179
|
export interface AuthenticationOptions {
|
|
17
180
|
/**
|
|
18
181
|
* Maximum token age in seconds (overrides exp claim validation)
|
|
@@ -42,11 +205,227 @@ export interface AuthenticationOptions {
|
|
|
42
205
|
audience?: string | string[];
|
|
43
206
|
};
|
|
44
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Class-based authentication middleware with comprehensive security features.
|
|
210
|
+
* Provides JWT validation, rate limiting, token blacklisting, and security logging.
|
|
211
|
+
*
|
|
212
|
+
* @template T - The type of user data returned by the token verification port
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* Basic JWT authentication:
|
|
216
|
+
* ```typescript
|
|
217
|
+
* import { Handler, AuthenticationMiddleware } from '@noony-serverless/core';
|
|
218
|
+
* import jwt from 'jsonwebtoken';
|
|
219
|
+
*
|
|
220
|
+
* interface User {
|
|
221
|
+
* id: string;
|
|
222
|
+
* email: string;
|
|
223
|
+
* roles: string[];
|
|
224
|
+
* }
|
|
225
|
+
*
|
|
226
|
+
* class JWTVerifier implements CustomTokenVerificationPort<User> {
|
|
227
|
+
* async verifyToken(token: string): Promise<User> {
|
|
228
|
+
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
|
|
229
|
+
* return {
|
|
230
|
+
* id: payload.sub,
|
|
231
|
+
* email: payload.email,
|
|
232
|
+
* roles: payload.roles || []
|
|
233
|
+
* };
|
|
234
|
+
* }
|
|
235
|
+
* }
|
|
236
|
+
*
|
|
237
|
+
* const protectedHandler = new Handler()
|
|
238
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
239
|
+
* .handle(async (request, context) => {
|
|
240
|
+
* const user = context.user as User;
|
|
241
|
+
* return {
|
|
242
|
+
* success: true,
|
|
243
|
+
* data: { message: `Hello ${user.email}`, userId: user.id }
|
|
244
|
+
* };
|
|
245
|
+
* });
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* Advanced authentication with security options:
|
|
250
|
+
* ```typescript
|
|
251
|
+
* const secureAuthMiddleware = new AuthenticationMiddleware(
|
|
252
|
+
* new JWTVerifier(),
|
|
253
|
+
* {
|
|
254
|
+
* maxTokenAge: 1800, // 30 minutes
|
|
255
|
+
* rateLimiting: {
|
|
256
|
+
* maxAttempts: 5,
|
|
257
|
+
* windowMs: 15 * 60 * 1000 // 15 minutes
|
|
258
|
+
* },
|
|
259
|
+
* isTokenBlacklisted: async (tokenId) => {
|
|
260
|
+
* return await redis.sismember('revoked_tokens', tokenId);
|
|
261
|
+
* },
|
|
262
|
+
* requiredClaims: {
|
|
263
|
+
* issuer: 'my-auth-server',
|
|
264
|
+
* audience: 'my-api'
|
|
265
|
+
* }
|
|
266
|
+
* }
|
|
267
|
+
* );
|
|
268
|
+
*
|
|
269
|
+
* const secureHandler = new Handler()
|
|
270
|
+
* .use(secureAuthMiddleware)
|
|
271
|
+
* .handle(async (request, context) => {
|
|
272
|
+
* // Only authenticated users reach here
|
|
273
|
+
* return { success: true, data: 'Secure data' };
|
|
274
|
+
* });
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* Google Cloud Functions integration:
|
|
279
|
+
* ```typescript
|
|
280
|
+
* import { http } from '@google-cloud/functions-framework';
|
|
281
|
+
*
|
|
282
|
+
* const userProfileHandler = new Handler()
|
|
283
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
284
|
+
* .handle(async (request, context) => {
|
|
285
|
+
* const user = context.user as User;
|
|
286
|
+
* const profile = await getUserProfile(user.id);
|
|
287
|
+
* return { success: true, data: profile };
|
|
288
|
+
* });
|
|
289
|
+
*
|
|
290
|
+
* export const getUserProfile = http('getUserProfile', (req, res) => {
|
|
291
|
+
* return userProfileHandler.execute(req, res);
|
|
292
|
+
* });
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
45
295
|
export declare class AuthenticationMiddleware<T> implements BaseMiddleware {
|
|
46
296
|
private tokenVerificationPort;
|
|
47
297
|
private options;
|
|
48
298
|
constructor(tokenVerificationPort: CustomTokenVerificationPort<T>, options?: AuthenticationOptions);
|
|
49
299
|
before(context: Context): Promise<void>;
|
|
50
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Factory function that creates an authentication middleware with token verification.
|
|
303
|
+
* Provides a functional approach for authentication setup.
|
|
304
|
+
*
|
|
305
|
+
* @template T - The type of user data returned by the token verification port
|
|
306
|
+
* @param tokenVerificationPort - The token verification implementation
|
|
307
|
+
* @param options - Authentication configuration options
|
|
308
|
+
* @returns A BaseMiddleware object with authentication logic
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* Simple JWT authentication:
|
|
312
|
+
* ```typescript
|
|
313
|
+
* import { Handler, verifyAuthTokenMiddleware } from '@noony-serverless/core';
|
|
314
|
+
*
|
|
315
|
+
* class SimpleJWTVerifier implements CustomTokenVerificationPort<{ userId: string }> {
|
|
316
|
+
* async verifyToken(token: string): Promise<{ userId: string }> {
|
|
317
|
+
* // Simple token verification logic
|
|
318
|
+
* if (token === 'valid-token') {
|
|
319
|
+
* return { userId: 'user-123' };
|
|
320
|
+
* }
|
|
321
|
+
* throw new Error('Invalid token');
|
|
322
|
+
* }
|
|
323
|
+
* }
|
|
324
|
+
*
|
|
325
|
+
* const handler = new Handler()
|
|
326
|
+
* .use(verifyAuthTokenMiddleware(new SimpleJWTVerifier()))
|
|
327
|
+
* .handle(async (request, context) => {
|
|
328
|
+
* const user = context.user as { userId: string };
|
|
329
|
+
* return { success: true, userId: user.userId };
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* API key authentication with rate limiting:
|
|
335
|
+
* ```typescript
|
|
336
|
+
* interface APIKeyUser {
|
|
337
|
+
* keyId: string;
|
|
338
|
+
* permissions: string[];
|
|
339
|
+
* organization: string;
|
|
340
|
+
* }
|
|
341
|
+
*
|
|
342
|
+
* class APIKeyVerifier implements CustomTokenVerificationPort<APIKeyUser> {
|
|
343
|
+
* async verifyToken(token: string): Promise<APIKeyUser> {
|
|
344
|
+
* const keyData = await this.validateAPIKey(token);
|
|
345
|
+
* if (!keyData) {
|
|
346
|
+
* throw new Error('Invalid API key');
|
|
347
|
+
* }
|
|
348
|
+
* return keyData;
|
|
349
|
+
* }
|
|
350
|
+
*
|
|
351
|
+
* private async validateAPIKey(key: string): Promise<APIKeyUser | null> {
|
|
352
|
+
* // Database lookup or external validation
|
|
353
|
+
* return {
|
|
354
|
+
* keyId: 'key-123',
|
|
355
|
+
* permissions: ['read', 'write'],
|
|
356
|
+
* organization: 'org-456'
|
|
357
|
+
* };
|
|
358
|
+
* }
|
|
359
|
+
* }
|
|
360
|
+
*
|
|
361
|
+
* const apiHandler = new Handler()
|
|
362
|
+
* .use(verifyAuthTokenMiddleware(
|
|
363
|
+
* new APIKeyVerifier(),
|
|
364
|
+
* {
|
|
365
|
+
* rateLimiting: {
|
|
366
|
+
* maxAttempts: 100,
|
|
367
|
+
* windowMs: 60 * 1000 // 1 minute
|
|
368
|
+
* }
|
|
369
|
+
* }
|
|
370
|
+
* ))
|
|
371
|
+
* .handle(async (request, context) => {
|
|
372
|
+
* const apiUser = context.user as APIKeyUser;
|
|
373
|
+
* return {
|
|
374
|
+
* success: true,
|
|
375
|
+
* data: { organization: apiUser.organization }
|
|
376
|
+
* };
|
|
377
|
+
* });
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* Express-style middleware chain:
|
|
382
|
+
* ```typescript
|
|
383
|
+
* import { Handler, verifyAuthTokenMiddleware, errorHandler } from '@noony-serverless/core';
|
|
384
|
+
*
|
|
385
|
+
* const authMiddleware = verifyAuthTokenMiddleware(
|
|
386
|
+
* new JWTVerifier(),
|
|
387
|
+
* {
|
|
388
|
+
* maxTokenAge: 3600,
|
|
389
|
+
* requiredClaims: {
|
|
390
|
+
* issuer: 'my-app',
|
|
391
|
+
* audience: 'api-users'
|
|
392
|
+
* }
|
|
393
|
+
* }
|
|
394
|
+
* );
|
|
395
|
+
*
|
|
396
|
+
* const protectedEndpoint = new Handler()
|
|
397
|
+
* .use(authMiddleware)
|
|
398
|
+
* .use(errorHandler())
|
|
399
|
+
* .handle(async (request, context) => {
|
|
400
|
+
* // Authenticated user available in context.user
|
|
401
|
+
* return { success: true, data: 'Protected resource' };
|
|
402
|
+
* });
|
|
403
|
+
* ```
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* Multiple authentication strategies:
|
|
407
|
+
* ```typescript
|
|
408
|
+
* // Different handlers for different auth types
|
|
409
|
+
* const jwtHandler = new Handler()
|
|
410
|
+
* .use(verifyAuthTokenMiddleware(new JWTVerifier()))
|
|
411
|
+
* .handle(jwtLogic);
|
|
412
|
+
*
|
|
413
|
+
* const apiKeyHandler = new Handler()
|
|
414
|
+
* .use(verifyAuthTokenMiddleware(new APIKeyVerifier()))
|
|
415
|
+
* .handle(apiKeyLogic);
|
|
416
|
+
*
|
|
417
|
+
* // Route based on authentication type
|
|
418
|
+
* export const handleRequest = (req: any, res: any) => {
|
|
419
|
+
* const authHeader = req.headers.authorization;
|
|
420
|
+
* if (authHeader?.startsWith('Bearer jwt.')) {
|
|
421
|
+
* return jwtHandler.execute(req, res);
|
|
422
|
+
* } else if (authHeader?.startsWith('Bearer ak_')) {
|
|
423
|
+
* return apiKeyHandler.execute(req, res);
|
|
424
|
+
* } else {
|
|
425
|
+
* res.status(401).json({ error: 'Authentication required' });
|
|
426
|
+
* }
|
|
427
|
+
* };
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
51
430
|
export declare const verifyAuthTokenMiddleware: <T>(tokenVerificationPort: CustomTokenVerificationPort<T>, options?: AuthenticationOptions) => BaseMiddleware;
|
|
52
431
|
//# sourceMappingURL=authenticationMiddleware.d.ts.map
|
|
@@ -169,6 +169,93 @@ async function verifyToken(tokenVerificationPort, context, options = {}) {
|
|
|
169
169
|
throw new errors_1.AuthenticationError('Invalid authentication');
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Class-based authentication middleware with comprehensive security features.
|
|
174
|
+
* Provides JWT validation, rate limiting, token blacklisting, and security logging.
|
|
175
|
+
*
|
|
176
|
+
* @template T - The type of user data returned by the token verification port
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* Basic JWT authentication:
|
|
180
|
+
* ```typescript
|
|
181
|
+
* import { Handler, AuthenticationMiddleware } from '@noony-serverless/core';
|
|
182
|
+
* import jwt from 'jsonwebtoken';
|
|
183
|
+
*
|
|
184
|
+
* interface User {
|
|
185
|
+
* id: string;
|
|
186
|
+
* email: string;
|
|
187
|
+
* roles: string[];
|
|
188
|
+
* }
|
|
189
|
+
*
|
|
190
|
+
* class JWTVerifier implements CustomTokenVerificationPort<User> {
|
|
191
|
+
* async verifyToken(token: string): Promise<User> {
|
|
192
|
+
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
|
|
193
|
+
* return {
|
|
194
|
+
* id: payload.sub,
|
|
195
|
+
* email: payload.email,
|
|
196
|
+
* roles: payload.roles || []
|
|
197
|
+
* };
|
|
198
|
+
* }
|
|
199
|
+
* }
|
|
200
|
+
*
|
|
201
|
+
* const protectedHandler = new Handler()
|
|
202
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
203
|
+
* .handle(async (request, context) => {
|
|
204
|
+
* const user = context.user as User;
|
|
205
|
+
* return {
|
|
206
|
+
* success: true,
|
|
207
|
+
* data: { message: `Hello ${user.email}`, userId: user.id }
|
|
208
|
+
* };
|
|
209
|
+
* });
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* Advanced authentication with security options:
|
|
214
|
+
* ```typescript
|
|
215
|
+
* const secureAuthMiddleware = new AuthenticationMiddleware(
|
|
216
|
+
* new JWTVerifier(),
|
|
217
|
+
* {
|
|
218
|
+
* maxTokenAge: 1800, // 30 minutes
|
|
219
|
+
* rateLimiting: {
|
|
220
|
+
* maxAttempts: 5,
|
|
221
|
+
* windowMs: 15 * 60 * 1000 // 15 minutes
|
|
222
|
+
* },
|
|
223
|
+
* isTokenBlacklisted: async (tokenId) => {
|
|
224
|
+
* return await redis.sismember('revoked_tokens', tokenId);
|
|
225
|
+
* },
|
|
226
|
+
* requiredClaims: {
|
|
227
|
+
* issuer: 'my-auth-server',
|
|
228
|
+
* audience: 'my-api'
|
|
229
|
+
* }
|
|
230
|
+
* }
|
|
231
|
+
* );
|
|
232
|
+
*
|
|
233
|
+
* const secureHandler = new Handler()
|
|
234
|
+
* .use(secureAuthMiddleware)
|
|
235
|
+
* .handle(async (request, context) => {
|
|
236
|
+
* // Only authenticated users reach here
|
|
237
|
+
* return { success: true, data: 'Secure data' };
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* Google Cloud Functions integration:
|
|
243
|
+
* ```typescript
|
|
244
|
+
* import { http } from '@google-cloud/functions-framework';
|
|
245
|
+
*
|
|
246
|
+
* const userProfileHandler = new Handler()
|
|
247
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
248
|
+
* .handle(async (request, context) => {
|
|
249
|
+
* const user = context.user as User;
|
|
250
|
+
* const profile = await getUserProfile(user.id);
|
|
251
|
+
* return { success: true, data: profile };
|
|
252
|
+
* });
|
|
253
|
+
*
|
|
254
|
+
* export const getUserProfile = http('getUserProfile', (req, res) => {
|
|
255
|
+
* return userProfileHandler.execute(req, res);
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
172
259
|
class AuthenticationMiddleware {
|
|
173
260
|
tokenVerificationPort;
|
|
174
261
|
options;
|
|
@@ -181,6 +268,135 @@ class AuthenticationMiddleware {
|
|
|
181
268
|
}
|
|
182
269
|
}
|
|
183
270
|
exports.AuthenticationMiddleware = AuthenticationMiddleware;
|
|
271
|
+
/**
|
|
272
|
+
* Factory function that creates an authentication middleware with token verification.
|
|
273
|
+
* Provides a functional approach for authentication setup.
|
|
274
|
+
*
|
|
275
|
+
* @template T - The type of user data returned by the token verification port
|
|
276
|
+
* @param tokenVerificationPort - The token verification implementation
|
|
277
|
+
* @param options - Authentication configuration options
|
|
278
|
+
* @returns A BaseMiddleware object with authentication logic
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* Simple JWT authentication:
|
|
282
|
+
* ```typescript
|
|
283
|
+
* import { Handler, verifyAuthTokenMiddleware } from '@noony-serverless/core';
|
|
284
|
+
*
|
|
285
|
+
* class SimpleJWTVerifier implements CustomTokenVerificationPort<{ userId: string }> {
|
|
286
|
+
* async verifyToken(token: string): Promise<{ userId: string }> {
|
|
287
|
+
* // Simple token verification logic
|
|
288
|
+
* if (token === 'valid-token') {
|
|
289
|
+
* return { userId: 'user-123' };
|
|
290
|
+
* }
|
|
291
|
+
* throw new Error('Invalid token');
|
|
292
|
+
* }
|
|
293
|
+
* }
|
|
294
|
+
*
|
|
295
|
+
* const handler = new Handler()
|
|
296
|
+
* .use(verifyAuthTokenMiddleware(new SimpleJWTVerifier()))
|
|
297
|
+
* .handle(async (request, context) => {
|
|
298
|
+
* const user = context.user as { userId: string };
|
|
299
|
+
* return { success: true, userId: user.userId };
|
|
300
|
+
* });
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* API key authentication with rate limiting:
|
|
305
|
+
* ```typescript
|
|
306
|
+
* interface APIKeyUser {
|
|
307
|
+
* keyId: string;
|
|
308
|
+
* permissions: string[];
|
|
309
|
+
* organization: string;
|
|
310
|
+
* }
|
|
311
|
+
*
|
|
312
|
+
* class APIKeyVerifier implements CustomTokenVerificationPort<APIKeyUser> {
|
|
313
|
+
* async verifyToken(token: string): Promise<APIKeyUser> {
|
|
314
|
+
* const keyData = await this.validateAPIKey(token);
|
|
315
|
+
* if (!keyData) {
|
|
316
|
+
* throw new Error('Invalid API key');
|
|
317
|
+
* }
|
|
318
|
+
* return keyData;
|
|
319
|
+
* }
|
|
320
|
+
*
|
|
321
|
+
* private async validateAPIKey(key: string): Promise<APIKeyUser | null> {
|
|
322
|
+
* // Database lookup or external validation
|
|
323
|
+
* return {
|
|
324
|
+
* keyId: 'key-123',
|
|
325
|
+
* permissions: ['read', 'write'],
|
|
326
|
+
* organization: 'org-456'
|
|
327
|
+
* };
|
|
328
|
+
* }
|
|
329
|
+
* }
|
|
330
|
+
*
|
|
331
|
+
* const apiHandler = new Handler()
|
|
332
|
+
* .use(verifyAuthTokenMiddleware(
|
|
333
|
+
* new APIKeyVerifier(),
|
|
334
|
+
* {
|
|
335
|
+
* rateLimiting: {
|
|
336
|
+
* maxAttempts: 100,
|
|
337
|
+
* windowMs: 60 * 1000 // 1 minute
|
|
338
|
+
* }
|
|
339
|
+
* }
|
|
340
|
+
* ))
|
|
341
|
+
* .handle(async (request, context) => {
|
|
342
|
+
* const apiUser = context.user as APIKeyUser;
|
|
343
|
+
* return {
|
|
344
|
+
* success: true,
|
|
345
|
+
* data: { organization: apiUser.organization }
|
|
346
|
+
* };
|
|
347
|
+
* });
|
|
348
|
+
* ```
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* Express-style middleware chain:
|
|
352
|
+
* ```typescript
|
|
353
|
+
* import { Handler, verifyAuthTokenMiddleware, errorHandler } from '@noony-serverless/core';
|
|
354
|
+
*
|
|
355
|
+
* const authMiddleware = verifyAuthTokenMiddleware(
|
|
356
|
+
* new JWTVerifier(),
|
|
357
|
+
* {
|
|
358
|
+
* maxTokenAge: 3600,
|
|
359
|
+
* requiredClaims: {
|
|
360
|
+
* issuer: 'my-app',
|
|
361
|
+
* audience: 'api-users'
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
* );
|
|
365
|
+
*
|
|
366
|
+
* const protectedEndpoint = new Handler()
|
|
367
|
+
* .use(authMiddleware)
|
|
368
|
+
* .use(errorHandler())
|
|
369
|
+
* .handle(async (request, context) => {
|
|
370
|
+
* // Authenticated user available in context.user
|
|
371
|
+
* return { success: true, data: 'Protected resource' };
|
|
372
|
+
* });
|
|
373
|
+
* ```
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* Multiple authentication strategies:
|
|
377
|
+
* ```typescript
|
|
378
|
+
* // Different handlers for different auth types
|
|
379
|
+
* const jwtHandler = new Handler()
|
|
380
|
+
* .use(verifyAuthTokenMiddleware(new JWTVerifier()))
|
|
381
|
+
* .handle(jwtLogic);
|
|
382
|
+
*
|
|
383
|
+
* const apiKeyHandler = new Handler()
|
|
384
|
+
* .use(verifyAuthTokenMiddleware(new APIKeyVerifier()))
|
|
385
|
+
* .handle(apiKeyLogic);
|
|
386
|
+
*
|
|
387
|
+
* // Route based on authentication type
|
|
388
|
+
* export const handleRequest = (req: any, res: any) => {
|
|
389
|
+
* const authHeader = req.headers.authorization;
|
|
390
|
+
* if (authHeader?.startsWith('Bearer jwt.')) {
|
|
391
|
+
* return jwtHandler.execute(req, res);
|
|
392
|
+
* } else if (authHeader?.startsWith('Bearer ak_')) {
|
|
393
|
+
* return apiKeyHandler.execute(req, res);
|
|
394
|
+
* } else {
|
|
395
|
+
* res.status(401).json({ error: 'Authentication required' });
|
|
396
|
+
* }
|
|
397
|
+
* };
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
184
400
|
const verifyAuthTokenMiddleware = (tokenVerificationPort, options = {}) => ({
|
|
185
401
|
async before(context) {
|
|
186
402
|
await verifyToken(tokenVerificationPort, context, options);
|