@flareone/common 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.
@@ -0,0 +1,577 @@
1
+ import { __decorateClass } from './chunk-XUS63JTZ.js';
2
+ import { Injectable, UnauthorizedException, ForbiddenException, defineMethodMetadata, getMethodMetadata } from '@flareone/core';
3
+
4
+ var AuthGuard = class {
5
+ async canActivate(context) {
6
+ const isPublic = context.getMetadata("isPublic");
7
+ if (isPublic) {
8
+ return true;
9
+ }
10
+ const request = context.getRequest();
11
+ const authHeader = request.headers.get("authorization");
12
+ if (!authHeader) {
13
+ throw new UnauthorizedException("Missing authorization header");
14
+ }
15
+ return this.validateToken(authHeader, context);
16
+ }
17
+ /**
18
+ * Override this method to implement token validation
19
+ */
20
+ async validateToken(_authHeader, _context) {
21
+ return true;
22
+ }
23
+ };
24
+ AuthGuard = __decorateClass([
25
+ Injectable()
26
+ ], AuthGuard);
27
+ var JwtGuard = class extends AuthGuard {
28
+ constructor(options = {}) {
29
+ super();
30
+ this.options = options;
31
+ }
32
+ async validateToken(authHeader, context) {
33
+ const prefix = this.options.tokenPrefix ?? "Bearer";
34
+ if (!authHeader.startsWith(`${prefix} `)) {
35
+ throw new UnauthorizedException("Invalid authorization format");
36
+ }
37
+ const token = authHeader.slice(prefix.length + 1);
38
+ try {
39
+ const payload = await this.verifyJwt(token);
40
+ context.setData("user", payload);
41
+ return true;
42
+ } catch {
43
+ throw new UnauthorizedException("Invalid or expired token");
44
+ }
45
+ }
46
+ async verifyJwt(token) {
47
+ const [headerB64, payloadB64, signatureB64] = token.split(".");
48
+ if (!headerB64 || !payloadB64 || !signatureB64) {
49
+ throw new Error("Invalid token format");
50
+ }
51
+ const payload = JSON.parse(atob(payloadB64.replace(/-/g, "+").replace(/_/g, "/")));
52
+ if (payload.exp && Date.now() >= payload.exp * 1e3) {
53
+ throw new Error("Token expired");
54
+ }
55
+ if (this.options.secret || this.options.secretKey) {
56
+ const isValid = await this.verifySignature(
57
+ `${headerB64}.${payloadB64}`,
58
+ signatureB64
59
+ );
60
+ if (!isValid) {
61
+ throw new Error("Invalid signature");
62
+ }
63
+ }
64
+ return payload;
65
+ }
66
+ async verifySignature(data, signature) {
67
+ const encoder = new TextEncoder();
68
+ let key;
69
+ if (this.options.secretKey) {
70
+ key = this.options.secretKey;
71
+ } else if (this.options.secret) {
72
+ key = await crypto.subtle.importKey(
73
+ "raw",
74
+ encoder.encode(this.options.secret),
75
+ { name: "HMAC", hash: "SHA-256" },
76
+ false,
77
+ ["verify"]
78
+ );
79
+ } else {
80
+ return true;
81
+ }
82
+ const signatureBytes = Uint8Array.from(
83
+ atob(signature.replace(/-/g, "+").replace(/_/g, "/")),
84
+ (c) => c.charCodeAt(0)
85
+ );
86
+ return crypto.subtle.verify(
87
+ "HMAC",
88
+ key,
89
+ signatureBytes,
90
+ encoder.encode(data)
91
+ );
92
+ }
93
+ };
94
+ JwtGuard = __decorateClass([
95
+ Injectable()
96
+ ], JwtGuard);
97
+ var ApiKeyGuard = class {
98
+ constructor(options = {}) {
99
+ this.options = options;
100
+ }
101
+ async canActivate(context) {
102
+ const isPublic = context.getMetadata("isPublic");
103
+ if (isPublic) {
104
+ return true;
105
+ }
106
+ const apiKey = this.extractApiKey(context);
107
+ if (!apiKey) {
108
+ throw new UnauthorizedException("API key is required");
109
+ }
110
+ const isValid = await this.validateKey(apiKey, context);
111
+ if (!isValid) {
112
+ throw new UnauthorizedException("Invalid API key");
113
+ }
114
+ return true;
115
+ }
116
+ extractApiKey(context) {
117
+ const request = context.getRequest();
118
+ const headerName = this.options.headerName ?? "x-api-key";
119
+ const headerKey = request.headers.get(headerName);
120
+ if (headerKey) return headerKey;
121
+ if (this.options.queryParam) {
122
+ const queryKey = context.getQueryParam(this.options.queryParam);
123
+ if (queryKey) return queryKey;
124
+ }
125
+ return null;
126
+ }
127
+ async validateKey(apiKey, _context) {
128
+ if (this.options.validKeys) {
129
+ return this.options.validKeys.includes(apiKey);
130
+ }
131
+ return true;
132
+ }
133
+ };
134
+ ApiKeyGuard = __decorateClass([
135
+ Injectable()
136
+ ], ApiKeyGuard);
137
+ var RolesGuard = class {
138
+ async canActivate(context) {
139
+ const requiredRoles = context.getMetadata("roles");
140
+ if (!requiredRoles || requiredRoles.length === 0) {
141
+ return true;
142
+ }
143
+ const user = context.getData("user");
144
+ if (!user || !user.roles) {
145
+ throw new ForbiddenException("Access denied");
146
+ }
147
+ const hasRole = requiredRoles.some((role) => user.roles.includes(role));
148
+ if (!hasRole) {
149
+ throw new ForbiddenException("Insufficient permissions");
150
+ }
151
+ return true;
152
+ }
153
+ };
154
+ RolesGuard = __decorateClass([
155
+ Injectable()
156
+ ], RolesGuard);
157
+ var ThrottleGuard = class {
158
+ storage = /* @__PURE__ */ new Map();
159
+ limit;
160
+ ttl;
161
+ cleanupInterval;
162
+ constructor(options = {}) {
163
+ this.limit = options.limit ?? 100;
164
+ this.ttl = options.ttl ?? 60;
165
+ this.startCleanup();
166
+ }
167
+ startCleanup() {
168
+ this.cleanupInterval = setInterval(() => {
169
+ const now = Date.now();
170
+ for (const [key, value] of this.storage.entries()) {
171
+ if (value.expiresAt < now) {
172
+ this.storage.delete(key);
173
+ }
174
+ }
175
+ }, 6e4);
176
+ }
177
+ destroy() {
178
+ if (this.cleanupInterval) {
179
+ clearInterval(this.cleanupInterval);
180
+ }
181
+ }
182
+ async canActivate(context) {
183
+ const skipThrottle = context.getMetadata("skipThrottle");
184
+ if (skipThrottle) {
185
+ return true;
186
+ }
187
+ const throttleConfig = context.getMetadata("throttle");
188
+ const limit = throttleConfig?.limit ?? this.limit;
189
+ const ttl = throttleConfig?.ttl ?? this.ttl;
190
+ const key = this.generateKey(context);
191
+ const now = Date.now();
192
+ const entry = this.storage.get(key);
193
+ if (entry && entry.expiresAt > now) {
194
+ if (entry.count >= limit) {
195
+ const response = new Response(JSON.stringify({
196
+ statusCode: 429,
197
+ message: "Too Many Requests"
198
+ }), {
199
+ status: 429,
200
+ headers: {
201
+ "Content-Type": "application/json",
202
+ "Retry-After": String(Math.ceil((entry.expiresAt - now) / 1e3)),
203
+ "X-RateLimit-Limit": String(limit),
204
+ "X-RateLimit-Remaining": "0",
205
+ "X-RateLimit-Reset": String(Math.ceil(entry.expiresAt / 1e3))
206
+ }
207
+ });
208
+ throw response;
209
+ }
210
+ entry.count++;
211
+ } else {
212
+ this.storage.set(key, {
213
+ count: 1,
214
+ expiresAt: now + ttl * 1e3
215
+ });
216
+ }
217
+ return true;
218
+ }
219
+ generateKey(context) {
220
+ const ip = context.getClientIp() ?? "unknown";
221
+ const path = context.getUrl().pathname;
222
+ return `throttle:${ip}:${path}`;
223
+ }
224
+ };
225
+ ThrottleGuard = __decorateClass([
226
+ Injectable()
227
+ ], ThrottleGuard);
228
+ var IpWhitelistGuard = class {
229
+ constructor(options) {
230
+ this.options = options;
231
+ }
232
+ async canActivate(context) {
233
+ const clientIp = context.getClientIp();
234
+ if (!clientIp) {
235
+ throw new ForbiddenException(this.options.message ?? "Access denied");
236
+ }
237
+ const isAllowed = this.options.ips.some((ip) => this.matchIp(clientIp, ip));
238
+ if (!isAllowed) {
239
+ throw new ForbiddenException(this.options.message ?? "Access denied");
240
+ }
241
+ return true;
242
+ }
243
+ matchIp(clientIp, pattern) {
244
+ if (pattern === "*") return true;
245
+ if (clientIp === pattern) return true;
246
+ if (pattern.includes("/")) {
247
+ return this.matchCIDR(clientIp, pattern);
248
+ }
249
+ if (pattern.includes("*")) {
250
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
251
+ return regex.test(clientIp);
252
+ }
253
+ return false;
254
+ }
255
+ matchCIDR(ip, cidr) {
256
+ const [range, bits] = cidr.split("/");
257
+ if (!range || !bits) return false;
258
+ const mask = parseInt(bits, 10);
259
+ if (isNaN(mask) || mask < 0 || mask > 32) return false;
260
+ const ipNum = this.ipToNumber(ip);
261
+ const rangeNum = this.ipToNumber(range);
262
+ if (ipNum === null || rangeNum === null) return false;
263
+ const maskNum = 4294967295 << 32 - mask >>> 0;
264
+ return (ipNum & maskNum) === (rangeNum & maskNum);
265
+ }
266
+ ipToNumber(ip) {
267
+ const parts = ip.split(".");
268
+ if (parts.length !== 4) return null;
269
+ let num = 0;
270
+ for (let i = 0; i < 4; i++) {
271
+ const part = parseInt(parts[i], 10);
272
+ if (isNaN(part) || part < 0 || part > 255) return null;
273
+ num = num << 8 | part;
274
+ }
275
+ return num >>> 0;
276
+ }
277
+ };
278
+ IpWhitelistGuard = __decorateClass([
279
+ Injectable()
280
+ ], IpWhitelistGuard);
281
+ var IpBlacklistGuard = class {
282
+ constructor(options) {
283
+ this.options = options;
284
+ }
285
+ async canActivate(context) {
286
+ const clientIp = context.getClientIp();
287
+ if (clientIp && this.options.ips.includes(clientIp)) {
288
+ throw new ForbiddenException(this.options.message ?? "Access denied");
289
+ }
290
+ return true;
291
+ }
292
+ };
293
+ IpBlacklistGuard = __decorateClass([
294
+ Injectable()
295
+ ], IpBlacklistGuard);
296
+ var KVThrottleGuard = class {
297
+ constructor(options) {
298
+ this.options = options;
299
+ this.limit = options.limit ?? 100;
300
+ this.ttl = options.ttl ?? 60;
301
+ this.keyPrefix = options.keyPrefix ?? "throttle:";
302
+ }
303
+ limit;
304
+ ttl;
305
+ keyPrefix;
306
+ async canActivate(context) {
307
+ const skipThrottle = context.getMetadata("skipThrottle");
308
+ if (skipThrottle) return true;
309
+ const key = this.options.keyGenerator ? this.options.keyGenerator(context) : this.getDefaultKey(context);
310
+ const fullKey = `${this.keyPrefix}${key}`;
311
+ const current = await this.options.kv.get(fullKey, "json");
312
+ const count = (current ?? 0) + 1;
313
+ if (count > this.limit) {
314
+ throw new ForbiddenException("Too many requests");
315
+ }
316
+ await this.options.kv.put(fullKey, JSON.stringify(count), {
317
+ expirationTtl: this.ttl
318
+ });
319
+ return true;
320
+ }
321
+ getDefaultKey(context) {
322
+ const clientIp = context.getClientIp();
323
+ const path = context.getRequest().url;
324
+ return `${clientIp}:${path}`;
325
+ }
326
+ };
327
+ KVThrottleGuard = __decorateClass([
328
+ Injectable()
329
+ ], KVThrottleGuard);
330
+ var DOThrottleGuard = class {
331
+ constructor(options) {
332
+ this.options = options;
333
+ this.limit = options.limit ?? 100;
334
+ this.ttl = options.ttl ?? 60;
335
+ }
336
+ limit;
337
+ ttl;
338
+ async canActivate(context) {
339
+ const skipThrottle = context.getMetadata("skipThrottle");
340
+ if (skipThrottle) return true;
341
+ const shardKey = this.options.shardKeyGenerator ? this.options.shardKeyGenerator(context) : this.getDefaultShardKey(context);
342
+ const id = this.options.namespace.idFromName(shardKey);
343
+ const stub = this.options.namespace.get(id);
344
+ const response = await stub.fetch("https://throttle/check", {
345
+ method: "POST",
346
+ headers: { "Content-Type": "application/json" },
347
+ body: JSON.stringify({
348
+ key: this.options.keyGenerator ? this.options.keyGenerator(context) : this.getDefaultKey(context),
349
+ limit: this.limit,
350
+ ttl: this.ttl
351
+ })
352
+ });
353
+ if (!response.ok) {
354
+ throw new ForbiddenException("Too many requests");
355
+ }
356
+ return true;
357
+ }
358
+ getDefaultKey(context) {
359
+ const clientIp = context.getClientIp();
360
+ const path = context.getRequest().url;
361
+ return `${clientIp}:${path}`;
362
+ }
363
+ getDefaultShardKey(context) {
364
+ const clientIp = context.getClientIp();
365
+ if (!clientIp) return "default";
366
+ const parts = clientIp.split(".");
367
+ return parts.slice(0, 3).join(".");
368
+ }
369
+ };
370
+ DOThrottleGuard = __decorateClass([
371
+ Injectable()
372
+ ], DOThrottleGuard);
373
+ var CFRateLimitGuard = class {
374
+ constructor(options) {
375
+ this.options = options;
376
+ }
377
+ async canActivate(context) {
378
+ const skipThrottle = context.getMetadata("skipThrottle");
379
+ if (skipThrottle) return true;
380
+ const key = this.options.keyGenerator ? this.options.keyGenerator(context) : this.getDefaultKey(context);
381
+ const { success } = await this.options.rateLimiter.limit({ key });
382
+ if (!success) {
383
+ throw new Response(JSON.stringify({
384
+ statusCode: 429,
385
+ message: "Too Many Requests"
386
+ }), {
387
+ status: 429,
388
+ headers: {
389
+ "Content-Type": "application/json",
390
+ "X-RateLimit-Limit": "configured in binding"
391
+ }
392
+ });
393
+ }
394
+ return true;
395
+ }
396
+ getDefaultKey(context) {
397
+ return context.getClientIp() ?? "unknown";
398
+ }
399
+ };
400
+ CFRateLimitGuard = __decorateClass([
401
+ Injectable()
402
+ ], CFRateLimitGuard);
403
+ var RATE_LIMIT_KEY = /* @__PURE__ */ Symbol("ratelimit:config");
404
+ function RateLimit(config) {
405
+ return (target, propertyKey, descriptor) => {
406
+ defineMethodMetadata(RATE_LIMIT_KEY, config, target.constructor, propertyKey);
407
+ return descriptor;
408
+ };
409
+ }
410
+ function getRateLimitConfig(target, propertyKey) {
411
+ return getMethodMetadata(RATE_LIMIT_KEY, target, propertyKey);
412
+ }
413
+ var FixedWindowRateLimiter = class {
414
+ store = /* @__PURE__ */ new Map();
415
+ check(key, limit, windowSeconds) {
416
+ const now = Date.now();
417
+ const windowMs = windowSeconds * 1e3;
418
+ const windowStart = Math.floor(now / windowMs) * windowMs;
419
+ const reset = Math.ceil((windowStart + windowMs) / 1e3);
420
+ let entry = this.store.get(key);
421
+ if (!entry || entry.windowStart !== windowStart) {
422
+ entry = { count: 0, windowStart };
423
+ }
424
+ if (entry.count >= limit) {
425
+ return {
426
+ allowed: false,
427
+ remaining: 0,
428
+ limit,
429
+ reset,
430
+ retryAfter: reset - Math.floor(now / 1e3)
431
+ };
432
+ }
433
+ entry.count++;
434
+ this.store.set(key, entry);
435
+ return {
436
+ allowed: true,
437
+ remaining: limit - entry.count,
438
+ limit,
439
+ reset
440
+ };
441
+ }
442
+ reset(key) {
443
+ this.store.delete(key);
444
+ }
445
+ clear() {
446
+ this.store.clear();
447
+ }
448
+ };
449
+ var SlidingWindowRateLimiter = class {
450
+ store = /* @__PURE__ */ new Map();
451
+ check(key, limit, windowSeconds) {
452
+ const now = Date.now();
453
+ const windowMs = windowSeconds * 1e3;
454
+ const windowStart = now - windowMs;
455
+ const reset = Math.ceil((now + windowMs) / 1e3);
456
+ let timestamps = this.store.get(key) ?? [];
457
+ timestamps = timestamps.filter((t) => t > windowStart);
458
+ if (timestamps.length >= limit) {
459
+ const oldestInWindow = timestamps[0];
460
+ const retryAfter = Math.ceil((oldestInWindow + windowMs - now) / 1e3);
461
+ return {
462
+ allowed: false,
463
+ remaining: 0,
464
+ limit,
465
+ reset,
466
+ retryAfter
467
+ };
468
+ }
469
+ timestamps.push(now);
470
+ this.store.set(key, timestamps);
471
+ return {
472
+ allowed: true,
473
+ remaining: limit - timestamps.length,
474
+ limit,
475
+ reset
476
+ };
477
+ }
478
+ reset(key) {
479
+ this.store.delete(key);
480
+ }
481
+ clear() {
482
+ this.store.clear();
483
+ }
484
+ };
485
+ var TokenBucketRateLimiter = class {
486
+ store = /* @__PURE__ */ new Map();
487
+ check(key, options) {
488
+ const now = Date.now();
489
+ const { capacity, refillRate, refillInterval } = options;
490
+ const refillMs = refillInterval * 1e3;
491
+ let bucket = this.store.get(key);
492
+ if (!bucket) {
493
+ bucket = { tokens: capacity, lastRefill: now };
494
+ }
495
+ const timeSinceRefill = now - bucket.lastRefill;
496
+ const tokensToAdd = Math.floor(timeSinceRefill / refillMs) * refillRate;
497
+ if (tokensToAdd > 0) {
498
+ bucket.tokens = Math.min(capacity, bucket.tokens + tokensToAdd);
499
+ bucket.lastRefill = now;
500
+ }
501
+ const reset = Math.ceil((now + refillMs) / 1e3);
502
+ if (bucket.tokens < 1) {
503
+ const waitTime = Math.ceil((refillMs - (now - bucket.lastRefill)) / 1e3);
504
+ return {
505
+ allowed: false,
506
+ remaining: 0,
507
+ limit: capacity,
508
+ reset,
509
+ retryAfter: waitTime
510
+ };
511
+ }
512
+ bucket.tokens--;
513
+ this.store.set(key, bucket);
514
+ return {
515
+ allowed: true,
516
+ remaining: bucket.tokens,
517
+ limit: capacity,
518
+ reset
519
+ };
520
+ }
521
+ reset(key) {
522
+ this.store.delete(key);
523
+ }
524
+ };
525
+ var RateLimitKeys = {
526
+ fromIp(request, options) {
527
+ const ip = request.headers.get("cf-connecting-ip") ?? request.headers.get("x-forwarded-for")?.split(",")[0] ?? "unknown";
528
+ return buildKey(ip, options);
529
+ },
530
+ fromUser(userId, options) {
531
+ return buildKey(`user:${userId}`, options);
532
+ },
533
+ fromApiKey(apiKey, options) {
534
+ return buildKey(`api:${apiKey}`, options);
535
+ },
536
+ fromPath(request, options) {
537
+ const url = new URL(request.url);
538
+ return buildKey(`path:${url.pathname}`, options);
539
+ },
540
+ fromIpAndPath(request, options) {
541
+ const ip = request.headers.get("cf-connecting-ip") ?? "unknown";
542
+ const url = new URL(request.url);
543
+ return buildKey(`${ip}:${url.pathname}`, options);
544
+ }
545
+ };
546
+ function buildKey(base, options) {
547
+ let key = base;
548
+ if (options?.prefix) key = `${options.prefix}:${key}`;
549
+ if (options?.suffix) key = `${key}:${options.suffix}`;
550
+ return key;
551
+ }
552
+ function createRateLimitHeaders(result) {
553
+ const headers = new Headers();
554
+ headers.set("X-RateLimit-Limit", String(result.limit));
555
+ headers.set("X-RateLimit-Remaining", String(result.remaining));
556
+ headers.set("X-RateLimit-Reset", String(result.reset));
557
+ if (!result.allowed && result.retryAfter) {
558
+ headers.set("Retry-After", String(result.retryAfter));
559
+ }
560
+ return headers;
561
+ }
562
+ function createRateLimitedResponse(result, message) {
563
+ return new Response(
564
+ JSON.stringify({
565
+ error: message ?? "Too Many Requests",
566
+ retryAfter: result.retryAfter
567
+ }),
568
+ {
569
+ status: 429,
570
+ headers: createRateLimitHeaders(result)
571
+ }
572
+ );
573
+ }
574
+
575
+ export { ApiKeyGuard, AuthGuard, CFRateLimitGuard, DOThrottleGuard, FixedWindowRateLimiter, IpBlacklistGuard, IpWhitelistGuard, JwtGuard, KVThrottleGuard, RateLimit, RateLimitKeys, RolesGuard, SlidingWindowRateLimiter, ThrottleGuard, TokenBucketRateLimiter, createRateLimitHeaders, createRateLimitedResponse, getRateLimitConfig };
576
+ //# sourceMappingURL=chunk-JAMM5H7G.js.map
577
+ //# sourceMappingURL=chunk-JAMM5H7G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/guards/index.ts"],"names":[],"mappings":"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","file":"chunk-JAMM5H7G.js","sourcesContent":["/**\r\n * Built-in Guards\r\n * authentication and other security stuff.\r\n */\r\n\r\nimport {\r\n type Guard,\r\n type ExecutionContextWrapper,\r\n Injectable,\r\n UnauthorizedException,\r\n ForbiddenException,\r\n defineMethodMetadata,\r\n getMethodMetadata,\r\n} from '@flareone/core';\r\n\r\n/**\r\n * Base authentication guard\r\n * Override canActivate to implement custom logic\r\n */\r\n@Injectable()\r\nexport class AuthGuard implements Guard {\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const isPublic = context.getMetadata<boolean>('isPublic');\r\n if (isPublic) {\r\n return true;\r\n }\r\n\r\n const request = context.getRequest();\r\n const authHeader = request.headers.get('authorization');\r\n\r\n if (!authHeader) {\r\n throw new UnauthorizedException('Missing authorization header');\r\n }\r\n\r\n return this.validateToken(authHeader, context);\r\n }\r\n\r\n /**\r\n * Override this method to implement token validation\r\n */\r\n protected async validateToken(\r\n _authHeader: string,\r\n _context: ExecutionContextWrapper\r\n ): Promise<boolean> {\r\n return true;\r\n }\r\n}\r\n\r\nexport interface JwtGuardOptions {\r\n secret?: string;\r\n secretKey?: CryptoKey;\r\n tokenPrefix?: string;\r\n}\r\n\r\n/**\r\n * JWT authentication guard\r\n */\r\n@Injectable()\r\nexport class JwtGuard extends AuthGuard {\r\n constructor(private readonly options: JwtGuardOptions = {}) {\r\n super();\r\n }\r\n\r\n protected async validateToken(\r\n authHeader: string,\r\n context: ExecutionContextWrapper\r\n ): Promise<boolean> {\r\n const prefix = this.options.tokenPrefix ?? 'Bearer';\r\n\r\n if (!authHeader.startsWith(`${prefix} `)) {\r\n throw new UnauthorizedException('Invalid authorization format');\r\n }\r\n\r\n const token = authHeader.slice(prefix.length + 1);\r\n\r\n try {\r\n const payload = await this.verifyJwt(token);\r\n context.setData('user', payload);\r\n return true;\r\n } catch {\r\n throw new UnauthorizedException('Invalid or expired token');\r\n }\r\n }\r\n\r\n private async verifyJwt(token: string): Promise<Record<string, unknown>> {\r\n const [headerB64, payloadB64, signatureB64] = token.split('.');\r\n\r\n if (!headerB64 || !payloadB64 || !signatureB64) {\r\n throw new Error('Invalid token format');\r\n }\r\n\r\n const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));\r\n\r\n if (payload.exp && Date.now() >= payload.exp * 1000) {\r\n throw new Error('Token expired');\r\n }\r\n\r\n if (this.options.secret || this.options.secretKey) {\r\n const isValid = await this.verifySignature(\r\n `${headerB64}.${payloadB64}`,\r\n signatureB64\r\n );\r\n\r\n if (!isValid) {\r\n throw new Error('Invalid signature');\r\n }\r\n }\r\n\r\n return payload;\r\n }\r\n\r\n private async verifySignature(data: string, signature: string): Promise<boolean> {\r\n const encoder = new TextEncoder();\r\n\r\n let key: CryptoKey;\r\n\r\n if (this.options.secretKey) {\r\n key = this.options.secretKey;\r\n } else if (this.options.secret) {\r\n key = await crypto.subtle.importKey(\r\n 'raw',\r\n encoder.encode(this.options.secret),\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['verify']\r\n );\r\n } else {\r\n return true;\r\n }\r\n\r\n const signatureBytes = Uint8Array.from(\r\n atob(signature.replace(/-/g, '+').replace(/_/g, '/')),\r\n (c) => c.charCodeAt(0)\r\n );\r\n\r\n return crypto.subtle.verify(\r\n 'HMAC',\r\n key,\r\n signatureBytes,\r\n encoder.encode(data)\r\n );\r\n }\r\n}\r\n\r\nexport interface ApiKeyGuardOptions {\r\n headerName?: string;\r\n queryParam?: string;\r\n validKeys?: string[];\r\n}\r\n\r\n/**\r\n * API Key authentication guard\r\n */\r\n@Injectable()\r\nexport class ApiKeyGuard implements Guard {\r\n constructor(private readonly options: ApiKeyGuardOptions = {}) { }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const isPublic = context.getMetadata<boolean>('isPublic');\r\n if (isPublic) {\r\n return true;\r\n }\r\n\r\n const apiKey = this.extractApiKey(context);\r\n\r\n if (!apiKey) {\r\n throw new UnauthorizedException('API key is required');\r\n }\r\n\r\n const isValid = await this.validateKey(apiKey, context);\r\n\r\n if (!isValid) {\r\n throw new UnauthorizedException('Invalid API key');\r\n }\r\n\r\n return true;\r\n }\r\n\r\n protected extractApiKey(context: ExecutionContextWrapper): string | null {\r\n const request = context.getRequest();\r\n\r\n const headerName = this.options.headerName ?? 'x-api-key';\r\n const headerKey = request.headers.get(headerName);\r\n if (headerKey) return headerKey;\r\n if (this.options.queryParam) {\r\n const queryKey = context.getQueryParam(this.options.queryParam);\r\n if (queryKey) return queryKey;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n protected async validateKey(\r\n apiKey: string,\r\n _context: ExecutionContextWrapper\r\n ): Promise<boolean> {\r\n if (this.options.validKeys) {\r\n return this.options.validKeys.includes(apiKey);\r\n }\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * Role-based access control guard\r\n */\r\n@Injectable()\r\nexport class RolesGuard implements Guard {\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const requiredRoles = context.getMetadata<string[]>('roles');\r\n\r\n if (!requiredRoles || requiredRoles.length === 0) {\r\n return true;\r\n }\r\n\r\n const user = context.getData<{ roles?: string[] }>('user');\r\n\r\n if (!user || !user.roles) {\r\n throw new ForbiddenException('Access denied');\r\n }\r\n\r\n const hasRole = requiredRoles.some((role: string) => user.roles!.includes(role));\r\n\r\n if (!hasRole) {\r\n throw new ForbiddenException('Insufficient permissions');\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n\r\nexport interface ThrottleGuardOptions {\r\n limit?: number;\r\n ttl?: number;\r\n keyGenerator?: (context: ExecutionContextWrapper) => string;\r\n}\r\n\r\n/**\r\n * Rate limiting guard with automatic cleanup\r\n * Uses in-memory storage with periodic cleanup of expired entries.\r\n * For production distributed rate limiting, use KV or Durable Objects.\r\n */\r\n@Injectable()\r\nexport class ThrottleGuard implements Guard {\r\n private readonly storage = new Map<string, { count: number; expiresAt: number }>();\r\n private readonly limit: number;\r\n private readonly ttl: number;\r\n private cleanupInterval?: ReturnType<typeof setInterval>;\r\n\r\n constructor(options: ThrottleGuardOptions = {}) {\r\n this.limit = options.limit ?? 100;\r\n this.ttl = options.ttl ?? 60;\r\n this.startCleanup();\r\n }\r\n\r\n private startCleanup(): void {\r\n this.cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, value] of this.storage.entries()) {\r\n if (value.expiresAt < now) {\r\n this.storage.delete(key);\r\n }\r\n }\r\n }, 60000);\r\n }\r\n\r\n destroy(): void {\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n }\r\n }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const skipThrottle = context.getMetadata<boolean>('skipThrottle');\r\n if (skipThrottle) {\r\n return true;\r\n }\r\n const throttleConfig = context.getMetadata<{ limit: number; ttl: number }>('throttle');\r\n const limit = throttleConfig?.limit ?? this.limit;\r\n const ttl = throttleConfig?.ttl ?? this.ttl;\r\n\r\n const key = this.generateKey(context);\r\n const now = Date.now();\r\n\r\n const entry = this.storage.get(key);\r\n\r\n if (entry && entry.expiresAt > now) {\r\n if (entry.count >= limit) {\r\n const response = new Response(JSON.stringify({\r\n statusCode: 429,\r\n message: 'Too Many Requests',\r\n }), {\r\n status: 429,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Retry-After': String(Math.ceil((entry.expiresAt - now) / 1000)),\r\n 'X-RateLimit-Limit': String(limit),\r\n 'X-RateLimit-Remaining': '0',\r\n 'X-RateLimit-Reset': String(Math.ceil(entry.expiresAt / 1000)),\r\n },\r\n });\r\n throw response;\r\n }\r\n\r\n entry.count++;\r\n } else {\r\n this.storage.set(key, {\r\n count: 1,\r\n expiresAt: now + ttl * 1000,\r\n });\r\n }\r\n\r\n return true;\r\n }\r\n\r\n private generateKey(context: ExecutionContextWrapper): string {\r\n const ip = context.getClientIp() ?? 'unknown';\r\n const path = context.getUrl().pathname;\r\n return `throttle:${ip}:${path}`;\r\n }\r\n}\r\n\r\nexport interface IpFilterOptions {\r\n ips: string[];\r\n message?: string;\r\n}\r\n\r\n/**\r\n * IP whitelist guard\r\n */\r\n@Injectable()\r\nexport class IpWhitelistGuard implements Guard {\r\n constructor(private readonly options: IpFilterOptions) { }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const clientIp = context.getClientIp();\r\n\r\n if (!clientIp) {\r\n throw new ForbiddenException(this.options.message ?? 'Access denied');\r\n }\r\n\r\n const isAllowed = this.options.ips.some((ip) => this.matchIp(clientIp, ip));\r\n\r\n if (!isAllowed) {\r\n throw new ForbiddenException(this.options.message ?? 'Access denied');\r\n }\r\n\r\n return true;\r\n }\r\n\r\n private matchIp(clientIp: string, pattern: string): boolean {\r\n if (pattern === '*') return true;\r\n if (clientIp === pattern) return true;\r\n\r\n if (pattern.includes('/')) {\r\n return this.matchCIDR(clientIp, pattern);\r\n }\r\n\r\n if (pattern.includes('*')) {\r\n const regex = new RegExp('^' + pattern.replace(/\\*/g, '.*') + '$');\r\n return regex.test(clientIp);\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private matchCIDR(ip: string, cidr: string): boolean {\r\n const [range, bits] = cidr.split('/');\r\n if (!range || !bits) return false;\r\n\r\n const mask = parseInt(bits, 10);\r\n if (isNaN(mask) || mask < 0 || mask > 32) return false;\r\n\r\n const ipNum = this.ipToNumber(ip);\r\n const rangeNum = this.ipToNumber(range);\r\n\r\n if (ipNum === null || rangeNum === null) return false;\r\n\r\n const maskNum = (0xFFFFFFFF << (32 - mask)) >>> 0;\r\n return (ipNum & maskNum) === (rangeNum & maskNum);\r\n }\r\n\r\n private ipToNumber(ip: string): number | null {\r\n const parts = ip.split('.');\r\n if (parts.length !== 4) return null;\r\n\r\n let num = 0;\r\n for (let i = 0; i < 4; i++) {\r\n const part = parseInt(parts[i]!, 10);\r\n if (isNaN(part) || part < 0 || part > 255) return null;\r\n num = (num << 8) | part;\r\n }\r\n return num >>> 0;\r\n }\r\n}\r\n\r\n/**\r\n * IP blacklist guard\r\n */\r\n@Injectable()\r\nexport class IpBlacklistGuard implements Guard {\r\n constructor(private readonly options: IpFilterOptions) { }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const clientIp = context.getClientIp();\r\n\r\n if (clientIp && this.options.ips.includes(clientIp)) {\r\n throw new ForbiddenException(this.options.message ?? 'Access denied');\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n\r\nexport interface KVThrottleOptions {\r\n kv: KVNamespace;\r\n limit?: number;\r\n ttl?: number;\r\n keyGenerator?: (context: ExecutionContextWrapper) => string;\r\n keyPrefix?: string;\r\n}\r\n\r\n@Injectable()\r\nexport class KVThrottleGuard implements Guard {\r\n private readonly limit: number;\r\n private readonly ttl: number;\r\n private readonly keyPrefix: string;\r\n\r\n constructor(private readonly options: KVThrottleOptions) {\r\n this.limit = options.limit ?? 100;\r\n this.ttl = options.ttl ?? 60;\r\n this.keyPrefix = options.keyPrefix ?? 'throttle:';\r\n }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const skipThrottle = context.getMetadata<boolean>('skipThrottle');\r\n if (skipThrottle) return true;\r\n\r\n const key = this.options.keyGenerator\r\n ? this.options.keyGenerator(context)\r\n : this.getDefaultKey(context);\r\n\r\n const fullKey = `${this.keyPrefix}${key}`;\r\n\r\n const current = await this.options.kv.get<number>(fullKey, 'json');\r\n const count = (current ?? 0) + 1;\r\n\r\n if (count > this.limit) {\r\n throw new ForbiddenException('Too many requests');\r\n }\r\n\r\n await this.options.kv.put(fullKey, JSON.stringify(count), {\r\n expirationTtl: this.ttl,\r\n });\r\n\r\n return true;\r\n }\r\n\r\n private getDefaultKey(context: ExecutionContextWrapper): string {\r\n const clientIp = context.getClientIp();\r\n const path = context.getRequest().url;\r\n return `${clientIp}:${path}`;\r\n }\r\n}\r\n\r\nexport interface DOThrottleOptions {\r\n namespace: DurableObjectNamespace;\r\n limit?: number;\r\n ttl?: number;\r\n keyGenerator?: (context: ExecutionContextWrapper) => string;\r\n shardKeyGenerator?: (context: ExecutionContextWrapper) => string;\r\n}\r\n\r\n@Injectable()\r\nexport class DOThrottleGuard implements Guard {\r\n private readonly limit: number;\r\n private readonly ttl: number;\r\n\r\n constructor(private readonly options: DOThrottleOptions) {\r\n this.limit = options.limit ?? 100;\r\n this.ttl = options.ttl ?? 60;\r\n }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const skipThrottle = context.getMetadata<boolean>('skipThrottle');\r\n if (skipThrottle) return true;\r\n\r\n const shardKey = this.options.shardKeyGenerator\r\n ? this.options.shardKeyGenerator(context)\r\n : this.getDefaultShardKey(context);\r\n\r\n const id = this.options.namespace.idFromName(shardKey);\r\n const stub = this.options.namespace.get(id);\r\n\r\n const response = await stub.fetch('https://throttle/check', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n key: this.options.keyGenerator\r\n ? this.options.keyGenerator(context)\r\n : this.getDefaultKey(context),\r\n limit: this.limit,\r\n ttl: this.ttl,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new ForbiddenException('Too many requests');\r\n }\r\n\r\n return true;\r\n }\r\n\r\n private getDefaultKey(context: ExecutionContextWrapper): string {\r\n const clientIp = context.getClientIp();\r\n const path = context.getRequest().url;\r\n return `${clientIp}:${path}`;\r\n }\r\n\r\n private getDefaultShardKey(context: ExecutionContextWrapper): string {\r\n const clientIp = context.getClientIp();\r\n if (!clientIp) return 'default';\r\n\r\n const parts = clientIp.split('.');\r\n return parts.slice(0, 3).join('.');\r\n }\r\n}\r\n\r\n/**\r\n * Cloudflare Rate Limiting binding interface\r\n */\r\nexport interface RateLimiter {\r\n limit(options: { key: string }): Promise<{ success: boolean }>;\r\n}\r\n\r\nexport interface CFRateLimitGuardOptions {\r\n rateLimiter: RateLimiter;\r\n keyGenerator?: (context: ExecutionContextWrapper) => string;\r\n}\r\n\r\n\r\n/**\r\n * Guard using Cloudflare's native Rate Limiting binding\r\n */\r\n@Injectable()\r\nexport class CFRateLimitGuard implements Guard {\r\n constructor(private readonly options: CFRateLimitGuardOptions) { }\r\n\r\n async canActivate(context: ExecutionContextWrapper): Promise<boolean> {\r\n const skipThrottle = context.getMetadata<boolean>('skipThrottle');\r\n if (skipThrottle) return true;\r\n\r\n const key = this.options.keyGenerator\r\n ? this.options.keyGenerator(context)\r\n : this.getDefaultKey(context);\r\n\r\n const { success } = await this.options.rateLimiter.limit({ key });\r\n\r\n if (!success) {\r\n throw new Response(JSON.stringify({\r\n statusCode: 429,\r\n message: 'Too Many Requests',\r\n }), {\r\n status: 429,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'X-RateLimit-Limit': 'configured in binding',\r\n },\r\n });\r\n }\r\n\r\n return true;\r\n }\r\n\r\n private getDefaultKey(context: ExecutionContextWrapper): string {\r\n return context.getClientIp() ?? 'unknown';\r\n }\r\n}\r\n\r\nconst RATE_LIMIT_KEY = Symbol('ratelimit:config');\r\n\r\nexport interface RateLimitConfig {\r\n limit: number;\r\n window: number;\r\n}\r\n\r\n/**\r\n * Decorator to apply rate limiting metadata to a method\r\n * Use with ThrottleGuard or a custom guard that reads this metadata\r\n */\r\nexport function RateLimit(config: RateLimitConfig): MethodDecorator {\r\n return (target, propertyKey, descriptor) => {\r\n defineMethodMetadata(RATE_LIMIT_KEY, config, target.constructor, propertyKey);\r\n return descriptor;\r\n };\r\n}\r\n\r\n/**\r\n * Get rate limit config from metadata\r\n */\r\nexport function getRateLimitConfig(target: object, propertyKey: string | symbol): RateLimitConfig | undefined {\r\n return getMethodMetadata<RateLimitConfig>(RATE_LIMIT_KEY, target, propertyKey);\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n limit: number;\r\n reset: number;\r\n retryAfter?: number;\r\n}\r\n\r\n/**\r\n * Fixed window rate limiter (in-memory)\r\n */\r\nexport class FixedWindowRateLimiter {\r\n private store = new Map<string, { count: number; windowStart: number }>();\r\n\r\n check(key: string, limit: number, windowSeconds: number): RateLimitResult {\r\n const now = Date.now();\r\n const windowMs = windowSeconds * 1000;\r\n const windowStart = Math.floor(now / windowMs) * windowMs;\r\n const reset = Math.ceil((windowStart + windowMs) / 1000);\r\n\r\n let entry = this.store.get(key);\r\n\r\n if (!entry || entry.windowStart !== windowStart) {\r\n entry = { count: 0, windowStart };\r\n }\r\n\r\n if (entry.count >= limit) {\r\n return {\r\n allowed: false,\r\n remaining: 0,\r\n limit,\r\n reset,\r\n retryAfter: reset - Math.floor(now / 1000),\r\n };\r\n }\r\n\r\n entry.count++;\r\n this.store.set(key, entry);\r\n\r\n return {\r\n allowed: true,\r\n remaining: limit - entry.count,\r\n limit,\r\n reset,\r\n };\r\n }\r\n\r\n reset(key: string): void {\r\n this.store.delete(key);\r\n }\r\n\r\n clear(): void {\r\n this.store.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Sliding window rate limiter (in-memory)\r\n * More accurate than fixed window but requires more storage\r\n */\r\nexport class SlidingWindowRateLimiter {\r\n private store = new Map<string, number[]>();\r\n\r\n check(key: string, limit: number, windowSeconds: number): RateLimitResult {\r\n const now = Date.now();\r\n const windowMs = windowSeconds * 1000;\r\n const windowStart = now - windowMs;\r\n const reset = Math.ceil((now + windowMs) / 1000);\r\n\r\n let timestamps = this.store.get(key) ?? [];\r\n timestamps = timestamps.filter((t) => t > windowStart);\r\n\r\n if (timestamps.length >= limit) {\r\n const oldestInWindow = timestamps[0]!;\r\n const retryAfter = Math.ceil((oldestInWindow + windowMs - now) / 1000);\r\n\r\n return {\r\n allowed: false,\r\n remaining: 0,\r\n limit,\r\n reset,\r\n retryAfter,\r\n };\r\n }\r\n\r\n timestamps.push(now);\r\n this.store.set(key, timestamps);\r\n\r\n return {\r\n allowed: true,\r\n remaining: limit - timestamps.length,\r\n limit,\r\n reset,\r\n };\r\n }\r\n\r\n reset(key: string): void {\r\n this.store.delete(key);\r\n }\r\n\r\n clear(): void {\r\n this.store.clear();\r\n }\r\n}\r\n\r\nexport interface TokenBucketOptions {\r\n capacity: number;\r\n refillRate: number;\r\n refillInterval: number;\r\n}\r\n\r\n/**\r\n * Token bucket rate limiter (in-memory)\r\n * Allows bursts while maintaining average rate\r\n */\r\nexport class TokenBucketRateLimiter {\r\n private store = new Map<string, { tokens: number; lastRefill: number }>();\r\n\r\n check(key: string, options: TokenBucketOptions): RateLimitResult {\r\n const now = Date.now();\r\n const { capacity, refillRate, refillInterval } = options;\r\n const refillMs = refillInterval * 1000;\r\n\r\n let bucket = this.store.get(key);\r\n\r\n if (!bucket) {\r\n bucket = { tokens: capacity, lastRefill: now };\r\n }\r\n\r\n const timeSinceRefill = now - bucket.lastRefill;\r\n const tokensToAdd = Math.floor(timeSinceRefill / refillMs) * refillRate;\r\n\r\n if (tokensToAdd > 0) {\r\n bucket.tokens = Math.min(capacity, bucket.tokens + tokensToAdd);\r\n bucket.lastRefill = now;\r\n }\r\n\r\n const reset = Math.ceil((now + refillMs) / 1000);\r\n\r\n if (bucket.tokens < 1) {\r\n const waitTime = Math.ceil((refillMs - (now - bucket.lastRefill)) / 1000);\r\n\r\n return {\r\n allowed: false,\r\n remaining: 0,\r\n limit: capacity,\r\n reset,\r\n retryAfter: waitTime,\r\n };\r\n }\r\n\r\n bucket.tokens--;\r\n this.store.set(key, bucket);\r\n\r\n return {\r\n allowed: true,\r\n remaining: bucket.tokens,\r\n limit: capacity,\r\n reset,\r\n };\r\n }\r\n\r\n reset(key: string): void {\r\n this.store.delete(key);\r\n }\r\n}\r\n\r\nexport interface RateLimitKeyOptions {\r\n prefix?: string;\r\n suffix?: string;\r\n}\r\n\r\n/**\r\n * Utility functions for building rate limit keys\r\n */\r\nexport const RateLimitKeys = {\r\n fromIp(request: Request, options?: RateLimitKeyOptions): string {\r\n const ip = request.headers.get('cf-connecting-ip') ??\r\n request.headers.get('x-forwarded-for')?.split(',')[0] ??\r\n 'unknown';\r\n return buildKey(ip, options);\r\n },\r\n\r\n fromUser(userId: string, options?: RateLimitKeyOptions): string {\r\n return buildKey(`user:${userId}`, options);\r\n },\r\n\r\n fromApiKey(apiKey: string, options?: RateLimitKeyOptions): string {\r\n return buildKey(`api:${apiKey}`, options);\r\n },\r\n\r\n fromPath(request: Request, options?: RateLimitKeyOptions): string {\r\n const url = new URL(request.url);\r\n return buildKey(`path:${url.pathname}`, options);\r\n },\r\n\r\n fromIpAndPath(request: Request, options?: RateLimitKeyOptions): string {\r\n const ip = request.headers.get('cf-connecting-ip') ?? 'unknown';\r\n const url = new URL(request.url);\r\n return buildKey(`${ip}:${url.pathname}`, options);\r\n },\r\n};\r\n\r\nfunction buildKey(base: string, options?: RateLimitKeyOptions): string {\r\n let key = base;\r\n if (options?.prefix) key = `${options.prefix}:${key}`;\r\n if (options?.suffix) key = `${key}:${options.suffix}`;\r\n return key;\r\n}\r\n\r\n/**\r\n * Create rate limit headers for a response\r\n */\r\nexport function createRateLimitHeaders(result: RateLimitResult): Headers {\r\n const headers = new Headers();\r\n headers.set('X-RateLimit-Limit', String(result.limit));\r\n headers.set('X-RateLimit-Remaining', String(result.remaining));\r\n headers.set('X-RateLimit-Reset', String(result.reset));\r\n\r\n if (!result.allowed && result.retryAfter) {\r\n headers.set('Retry-After', String(result.retryAfter));\r\n }\r\n\r\n return headers;\r\n}\r\n\r\n/**\r\n * Create a 429 Too Many Requests response\r\n */\r\nexport function createRateLimitedResponse(result: RateLimitResult, message?: string): Response {\r\n return new Response(\r\n JSON.stringify({\r\n error: message ?? 'Too Many Requests',\r\n retryAfter: result.retryAfter,\r\n }),\r\n {\r\n status: 429,\r\n headers: createRateLimitHeaders(result),\r\n }\r\n );\r\n}\r\n"]}