@gagandeep023/api-gateway 0.3.1 → 0.4.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.
@@ -1,5 +1,5 @@
1
1
  import { Router, Request, Response, NextFunction } from 'express';
2
- import { RateLimitConfig, RequestLog, GatewayAnalytics, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.mjs';
2
+ import { RateLimitConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.mjs';
3
3
 
4
4
  declare class RateLimiterService {
5
5
  private tokenBucket;
@@ -29,9 +29,43 @@ declare class AnalyticsService {
29
29
  private getOrderedLogs;
30
30
  }
31
31
 
32
+ declare class DeviceRegistryService {
33
+ private devices;
34
+ private filePath;
35
+ private writeTimeout;
36
+ private cleanupInterval;
37
+ private registrationAttempts;
38
+ constructor(filePath: string);
39
+ private loadFromDisk;
40
+ private saveToDiskSync;
41
+ private debouncedSave;
42
+ private cleanupExpired;
43
+ private getActiveCountByIp;
44
+ private checkRateLimit;
45
+ private recordAttempt;
46
+ registerDevice(browserId: string, ip: string, userAgent: string): {
47
+ success: true;
48
+ device: DeviceEntry;
49
+ } | {
50
+ success: false;
51
+ error: string;
52
+ status: number;
53
+ };
54
+ getDevice(browserId: string): DeviceEntry | null;
55
+ updateLastSeen(browserId: string, ip: string): void;
56
+ revokeDevice(browserId: string): boolean;
57
+ getStats(): {
58
+ total: number;
59
+ active: number;
60
+ expired: number;
61
+ };
62
+ destroy(): void;
63
+ }
64
+
32
65
  interface GatewayInstances {
33
66
  rateLimiterService: RateLimiterService;
34
67
  analyticsService: AnalyticsService;
68
+ deviceRegistry?: DeviceRegistryService;
35
69
  middleware: Router;
36
70
  config: Required<GatewayMiddlewareConfig>;
37
71
  }
@@ -44,7 +78,12 @@ interface GatewayRoutesOptions {
44
78
  }
45
79
  declare function createGatewayRoutes(options: GatewayRoutesOptions): Router;
46
80
 
47
- declare function createApiKeyAuth(getKeys: () => ApiKeysConfig): (req: Request, res: Response, next: NextFunction) => void;
81
+ interface DeviceAuthRoutesOptions {
82
+ deviceRegistry: DeviceRegistryService;
83
+ }
84
+ declare function createDeviceAuthRoutes(options: DeviceAuthRoutesOptions): Router;
85
+
86
+ declare function createApiKeyAuth(getKeys: () => ApiKeysConfig, deviceRegistry?: DeviceRegistryService): (req: Request, res: Response, next: NextFunction) => void;
48
87
 
49
88
  declare function createIpFilter(getRules: () => IpRules): (req: Request, res: Response, next: NextFunction) => void;
50
89
 
@@ -52,4 +91,13 @@ declare function createRateLimiter(service: RateLimiterService): (req: Request,
52
91
 
53
92
  declare function createRequestLogger(analytics: AnalyticsService): (req: Request, res: Response, next: NextFunction) => void;
54
93
 
55
- export { AnalyticsService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger };
94
+ declare function generateTOTP(browserId: string, secret: string, timeOffset?: number): string;
95
+ declare function validateTOTP(browserId: string, secret: string, providedCode: string): boolean;
96
+ declare function formatKey(browserId: string, code: string): string;
97
+ declare function parseKey(key: string): {
98
+ browserId: string;
99
+ code: string;
100
+ } | null;
101
+ declare function generateSecret(): string;
102
+
103
+ export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
@@ -1,5 +1,5 @@
1
1
  import { Router, Request, Response, NextFunction } from 'express';
2
- import { RateLimitConfig, RequestLog, GatewayAnalytics, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.js';
2
+ import { RateLimitConfig, RequestLog, GatewayAnalytics, DeviceEntry, GatewayMiddlewareConfig, ApiKeysConfig, IpRules } from '../types/index.js';
3
3
 
4
4
  declare class RateLimiterService {
5
5
  private tokenBucket;
@@ -29,9 +29,43 @@ declare class AnalyticsService {
29
29
  private getOrderedLogs;
30
30
  }
31
31
 
32
+ declare class DeviceRegistryService {
33
+ private devices;
34
+ private filePath;
35
+ private writeTimeout;
36
+ private cleanupInterval;
37
+ private registrationAttempts;
38
+ constructor(filePath: string);
39
+ private loadFromDisk;
40
+ private saveToDiskSync;
41
+ private debouncedSave;
42
+ private cleanupExpired;
43
+ private getActiveCountByIp;
44
+ private checkRateLimit;
45
+ private recordAttempt;
46
+ registerDevice(browserId: string, ip: string, userAgent: string): {
47
+ success: true;
48
+ device: DeviceEntry;
49
+ } | {
50
+ success: false;
51
+ error: string;
52
+ status: number;
53
+ };
54
+ getDevice(browserId: string): DeviceEntry | null;
55
+ updateLastSeen(browserId: string, ip: string): void;
56
+ revokeDevice(browserId: string): boolean;
57
+ getStats(): {
58
+ total: number;
59
+ active: number;
60
+ expired: number;
61
+ };
62
+ destroy(): void;
63
+ }
64
+
32
65
  interface GatewayInstances {
33
66
  rateLimiterService: RateLimiterService;
34
67
  analyticsService: AnalyticsService;
68
+ deviceRegistry?: DeviceRegistryService;
35
69
  middleware: Router;
36
70
  config: Required<GatewayMiddlewareConfig>;
37
71
  }
@@ -44,7 +78,12 @@ interface GatewayRoutesOptions {
44
78
  }
45
79
  declare function createGatewayRoutes(options: GatewayRoutesOptions): Router;
46
80
 
47
- declare function createApiKeyAuth(getKeys: () => ApiKeysConfig): (req: Request, res: Response, next: NextFunction) => void;
81
+ interface DeviceAuthRoutesOptions {
82
+ deviceRegistry: DeviceRegistryService;
83
+ }
84
+ declare function createDeviceAuthRoutes(options: DeviceAuthRoutesOptions): Router;
85
+
86
+ declare function createApiKeyAuth(getKeys: () => ApiKeysConfig, deviceRegistry?: DeviceRegistryService): (req: Request, res: Response, next: NextFunction) => void;
48
87
 
49
88
  declare function createIpFilter(getRules: () => IpRules): (req: Request, res: Response, next: NextFunction) => void;
50
89
 
@@ -52,4 +91,13 @@ declare function createRateLimiter(service: RateLimiterService): (req: Request,
52
91
 
53
92
  declare function createRequestLogger(analytics: AnalyticsService): (req: Request, res: Response, next: NextFunction) => void;
54
93
 
55
- export { AnalyticsService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger };
94
+ declare function generateTOTP(browserId: string, secret: string, timeOffset?: number): string;
95
+ declare function validateTOTP(browserId: string, secret: string, providedCode: string): boolean;
96
+ declare function formatKey(browserId: string, code: string): string;
97
+ declare function parseKey(key: string): {
98
+ browserId: string;
99
+ code: string;
100
+ } | null;
101
+ declare function generateSecret(): string;
102
+
103
+ export { AnalyticsService, type DeviceAuthRoutesOptions, DeviceRegistryService, type GatewayInstances, type GatewayRoutesOptions, RateLimiterService, createApiKeyAuth, createDeviceAuthRoutes, createGatewayMiddleware, createGatewayRoutes, createIpFilter, createRateLimiter, createRequestLogger, formatKey, generateSecret, generateTOTP, parseKey, validateTOTP };
@@ -31,13 +31,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var backend_exports = {};
32
32
  __export(backend_exports, {
33
33
  AnalyticsService: () => AnalyticsService,
34
+ DeviceRegistryService: () => DeviceRegistryService,
34
35
  RateLimiterService: () => RateLimiterService,
35
36
  createApiKeyAuth: () => createApiKeyAuth,
37
+ createDeviceAuthRoutes: () => createDeviceAuthRoutes,
36
38
  createGatewayMiddleware: () => createGatewayMiddleware,
37
39
  createGatewayRoutes: () => createGatewayRoutes,
38
40
  createIpFilter: () => createIpFilter,
39
41
  createRateLimiter: () => createRateLimiter,
40
- createRequestLogger: () => createRequestLogger
42
+ createRequestLogger: () => createRequestLogger,
43
+ formatKey: () => formatKey,
44
+ generateSecret: () => generateSecret,
45
+ generateTOTP: () => generateTOTP,
46
+ parseKey: () => parseKey,
47
+ validateTOTP: () => validateTOTP
41
48
  });
42
49
  module.exports = __toCommonJS(backend_exports);
43
50
 
@@ -201,7 +208,7 @@ var AnalyticsService = class {
201
208
  const current = endpointCounts.get(log.path) || 0;
202
209
  endpointCounts.set(log.path, current + 1);
203
210
  }
204
- const topEndpoints = Array.from(endpointCounts.entries()).map(([path, count]) => ({ path, count })).sort((a, b) => b.count - a.count).slice(0, 5);
211
+ const topEndpoints = Array.from(endpointCounts.entries()).map(([path2, count]) => ({ path: path2, count })).sort((a, b) => b.count - a.count).slice(0, 5);
205
212
  const errorCount = ordered.filter((l) => l.statusCode >= 400).length;
206
213
  const errorRate = this.count > 0 ? errorCount / this.count * 100 : 0;
207
214
  const totalResponseTime = ordered.reduce((sum, l) => sum + l.responseTime, 0);
@@ -235,6 +242,226 @@ var AnalyticsService = class {
235
242
  }
236
243
  };
237
244
 
245
+ // src/backend/services/DeviceRegistryService.ts
246
+ var import_fs = __toESM(require("fs"));
247
+ var import_path = __toESM(require("path"));
248
+
249
+ // src/backend/utils/totp.ts
250
+ var import_crypto = __toESM(require("crypto"));
251
+ var TIME_WINDOW_MS = 3600 * 1e3;
252
+ var CODE_LENGTH = 16;
253
+ function generateTOTP(browserId, secret, timeOffset = 0) {
254
+ const timeWindow = Math.floor(Date.now() / TIME_WINDOW_MS) + timeOffset;
255
+ const message = `${browserId}:${timeWindow}`;
256
+ const hmac = import_crypto.default.createHmac("sha256", secret).update(message).digest("hex");
257
+ return hmac.substring(0, CODE_LENGTH);
258
+ }
259
+ function validateTOTP(browserId, secret, providedCode) {
260
+ const currentCode = generateTOTP(browserId, secret, 0);
261
+ const previousCode = generateTOTP(browserId, secret, -1);
262
+ return timingSafeEqual(providedCode, currentCode) || timingSafeEqual(providedCode, previousCode);
263
+ }
264
+ function timingSafeEqual(a, b) {
265
+ if (a.length !== b.length) return false;
266
+ const bufA = Buffer.from(a);
267
+ const bufB = Buffer.from(b);
268
+ return import_crypto.default.timingSafeEqual(bufA, bufB);
269
+ }
270
+ function formatKey(browserId, code) {
271
+ return `totp_${browserId}_${code}`;
272
+ }
273
+ function parseKey(key) {
274
+ if (!key.startsWith("totp_")) return null;
275
+ const parts = key.slice(5).split("_");
276
+ if (parts.length < 2) return null;
277
+ const code = parts[parts.length - 1];
278
+ const browserId = parts.slice(0, -1).join("_");
279
+ if (!browserId || !code) return null;
280
+ return { browserId, code };
281
+ }
282
+ function generateSecret() {
283
+ return import_crypto.default.randomBytes(32).toString("hex");
284
+ }
285
+
286
+ // src/backend/services/DeviceRegistryService.ts
287
+ var ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
288
+ var CLEANUP_INTERVAL_MS = 60 * 60 * 1e3;
289
+ var DEBOUNCE_WRITE_MS = 2e3;
290
+ var MAX_REGISTRATIONS_PER_IP_PER_MIN = 10;
291
+ var MAX_REGISTRATIONS_PER_IP_TOTAL = 30;
292
+ var RATE_WINDOW_MS = 60 * 1e3;
293
+ var DeviceRegistryService = class {
294
+ devices = /* @__PURE__ */ new Map();
295
+ filePath;
296
+ writeTimeout = null;
297
+ cleanupInterval = null;
298
+ // In-memory rate tracking: ip -> timestamps of recent registration attempts
299
+ registrationAttempts = /* @__PURE__ */ new Map();
300
+ constructor(filePath) {
301
+ this.filePath = filePath;
302
+ this.loadFromDisk();
303
+ this.cleanupExpired();
304
+ this.cleanupInterval = setInterval(() => this.cleanupExpired(), CLEANUP_INTERVAL_MS);
305
+ }
306
+ loadFromDisk() {
307
+ try {
308
+ if (import_fs.default.existsSync(this.filePath)) {
309
+ const raw = import_fs.default.readFileSync(this.filePath, "utf-8");
310
+ const data = JSON.parse(raw);
311
+ for (const device of data.devices) {
312
+ this.devices.set(device.browserId, device);
313
+ }
314
+ console.log(`[DeviceRegistry] Loaded ${this.devices.size} devices from ${this.filePath}`);
315
+ } else {
316
+ const dir = import_path.default.dirname(this.filePath);
317
+ if (!import_fs.default.existsSync(dir)) {
318
+ import_fs.default.mkdirSync(dir, { recursive: true });
319
+ }
320
+ this.saveToDiskSync();
321
+ console.log(`[DeviceRegistry] Created new devices file at ${this.filePath}`);
322
+ }
323
+ } catch (err) {
324
+ console.error("[DeviceRegistry] Failed to load devices file:", err);
325
+ }
326
+ }
327
+ saveToDiskSync() {
328
+ const data = {
329
+ devices: Array.from(this.devices.values())
330
+ };
331
+ import_fs.default.writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf-8");
332
+ }
333
+ debouncedSave() {
334
+ if (this.writeTimeout) clearTimeout(this.writeTimeout);
335
+ this.writeTimeout = setTimeout(() => {
336
+ this.saveToDiskSync();
337
+ }, DEBOUNCE_WRITE_MS);
338
+ }
339
+ cleanupExpired() {
340
+ const now = Date.now();
341
+ let removed = 0;
342
+ for (const [id, device] of this.devices) {
343
+ if (new Date(device.expiresAt).getTime() <= now) {
344
+ this.devices.delete(id);
345
+ removed++;
346
+ }
347
+ }
348
+ if (removed > 0) {
349
+ console.log(`[DeviceRegistry] Cleaned up ${removed} expired devices`);
350
+ this.debouncedSave();
351
+ }
352
+ }
353
+ getActiveCountByIp(ip) {
354
+ const now = Date.now();
355
+ let count = 0;
356
+ for (const device of this.devices.values()) {
357
+ if (device.ip === ip && device.active && new Date(device.expiresAt).getTime() > now) {
358
+ count++;
359
+ }
360
+ }
361
+ return count;
362
+ }
363
+ checkRateLimit(ip) {
364
+ const now = Date.now();
365
+ const attempts = this.registrationAttempts.get(ip) || [];
366
+ const recent = attempts.filter((t) => now - t < RATE_WINDOW_MS);
367
+ this.registrationAttempts.set(ip, recent);
368
+ return recent.length < MAX_REGISTRATIONS_PER_IP_PER_MIN;
369
+ }
370
+ recordAttempt(ip) {
371
+ const attempts = this.registrationAttempts.get(ip) || [];
372
+ attempts.push(Date.now());
373
+ this.registrationAttempts.set(ip, attempts);
374
+ }
375
+ registerDevice(browserId, ip, userAgent) {
376
+ if (!this.checkRateLimit(ip)) {
377
+ return {
378
+ success: false,
379
+ error: "Registration rate limit exceeded. Max 10 per minute per IP.",
380
+ status: 429
381
+ };
382
+ }
383
+ this.recordAttempt(ip);
384
+ if (this.getActiveCountByIp(ip) >= MAX_REGISTRATIONS_PER_IP_TOTAL) {
385
+ return {
386
+ success: false,
387
+ error: "Maximum device registrations reached for this IP. Max 30 per IP.",
388
+ status: 403
389
+ };
390
+ }
391
+ const existing = this.devices.get(browserId);
392
+ if (existing && existing.active && new Date(existing.expiresAt).getTime() > Date.now()) {
393
+ existing.expiresAt = new Date(Date.now() + ONE_WEEK_MS).toISOString();
394
+ existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
395
+ existing.lastIp = ip;
396
+ this.debouncedSave();
397
+ return { success: true, device: existing };
398
+ }
399
+ const now = /* @__PURE__ */ new Date();
400
+ const device = {
401
+ browserId,
402
+ sharedSecret: generateSecret(),
403
+ ip,
404
+ userAgent,
405
+ registeredAt: now.toISOString(),
406
+ expiresAt: new Date(now.getTime() + ONE_WEEK_MS).toISOString(),
407
+ lastSeen: now.toISOString(),
408
+ lastIp: ip,
409
+ active: true
410
+ };
411
+ this.devices.set(browserId, device);
412
+ this.debouncedSave();
413
+ return { success: true, device };
414
+ }
415
+ getDevice(browserId) {
416
+ const device = this.devices.get(browserId) || null;
417
+ if (!device) return null;
418
+ if (new Date(device.expiresAt).getTime() <= Date.now()) {
419
+ this.devices.delete(browserId);
420
+ this.debouncedSave();
421
+ return null;
422
+ }
423
+ if (!device.active) return null;
424
+ return device;
425
+ }
426
+ updateLastSeen(browserId, ip) {
427
+ const device = this.devices.get(browserId);
428
+ if (!device) return;
429
+ device.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
430
+ if (device.lastIp !== ip) {
431
+ console.log(`[DeviceRegistry] IP change for ${browserId}: ${device.lastIp} -> ${ip}`);
432
+ device.lastIp = ip;
433
+ }
434
+ this.debouncedSave();
435
+ }
436
+ revokeDevice(browserId) {
437
+ const device = this.devices.get(browserId);
438
+ if (!device) return false;
439
+ device.active = false;
440
+ this.debouncedSave();
441
+ return true;
442
+ }
443
+ getStats() {
444
+ const now = Date.now();
445
+ let active = 0;
446
+ let expired = 0;
447
+ for (const device of this.devices.values()) {
448
+ if (!device.active || new Date(device.expiresAt).getTime() <= now) {
449
+ expired++;
450
+ } else {
451
+ active++;
452
+ }
453
+ }
454
+ return { total: this.devices.size, active, expired };
455
+ }
456
+ destroy() {
457
+ if (this.cleanupInterval) clearInterval(this.cleanupInterval);
458
+ if (this.writeTimeout) {
459
+ clearTimeout(this.writeTimeout);
460
+ this.saveToDiskSync();
461
+ }
462
+ }
463
+ };
464
+
238
465
  // src/config/defaults.ts
239
466
  var DEFAULT_RATE_LIMIT_CONFIG = {
240
467
  tiers: {
@@ -255,7 +482,7 @@ var DEFAULT_API_KEYS = {
255
482
  };
256
483
 
257
484
  // src/backend/middleware/apiKeyAuth.ts
258
- function createApiKeyAuth(getKeys) {
485
+ function createApiKeyAuth(getKeys, deviceRegistry) {
259
486
  return function apiKeyAuth(req, res, next) {
260
487
  const apiKey = req.header("X-API-Key") || req.query.apiKey;
261
488
  if (!apiKey) {
@@ -264,6 +491,29 @@ function createApiKeyAuth(getKeys) {
264
491
  next();
265
492
  return;
266
493
  }
494
+ if (apiKey.startsWith("totp_") && deviceRegistry) {
495
+ const parsed = parseKey(apiKey);
496
+ if (!parsed) {
497
+ res.status(401).json({ error: "Malformed TOTP key" });
498
+ return;
499
+ }
500
+ const device = deviceRegistry.getDevice(parsed.browserId);
501
+ if (!device) {
502
+ res.status(401).json({ error: "Device not registered or expired" });
503
+ return;
504
+ }
505
+ if (!validateTOTP(parsed.browserId, device.sharedSecret, parsed.code)) {
506
+ res.status(401).json({ error: "Invalid or expired TOTP code" });
507
+ return;
508
+ }
509
+ const ip = req.ip || req.socket.remoteAddress || "unknown";
510
+ deviceRegistry.updateLastSeen(parsed.browserId, ip);
511
+ req.clientId = parsed.browserId;
512
+ req.tier = "free";
513
+ req.apiKeyValue = apiKey;
514
+ next();
515
+ return;
516
+ }
267
517
  const config = getKeys();
268
518
  const keyEntry = config.keys.find((k) => k.key === apiKey && k.active);
269
519
  if (!keyEntry) {
@@ -348,18 +598,24 @@ function createGatewayMiddleware(userConfig) {
348
598
  const config = {
349
599
  rateLimits: userConfig?.rateLimits ?? DEFAULT_RATE_LIMIT_CONFIG,
350
600
  ipRules: userConfig?.ipRules ?? DEFAULT_IP_RULES,
351
- apiKeys: userConfig?.apiKeys ?? DEFAULT_API_KEYS
601
+ apiKeys: userConfig?.apiKeys ?? DEFAULT_API_KEYS,
602
+ deviceRegistryPath: userConfig?.deviceRegistryPath ?? ""
352
603
  };
353
604
  const rateLimiterService = new RateLimiterService(config.rateLimits);
354
605
  const analyticsService = new AnalyticsService();
606
+ let deviceRegistry;
607
+ if (config.deviceRegistryPath) {
608
+ deviceRegistry = new DeviceRegistryService(config.deviceRegistryPath);
609
+ }
355
610
  const router = (0, import_express.Router)();
356
611
  router.use(createRequestLogger(analyticsService));
357
- router.use(createApiKeyAuth(() => config.apiKeys));
612
+ router.use(createApiKeyAuth(() => config.apiKeys, deviceRegistry));
358
613
  router.use(createIpFilter(() => config.ipRules));
359
614
  router.use(createRateLimiter(rateLimiterService));
360
615
  return {
361
616
  rateLimiterService,
362
617
  analyticsService,
618
+ deviceRegistry,
363
619
  middleware: router,
364
620
  config
365
621
  };
@@ -367,7 +623,7 @@ function createGatewayMiddleware(userConfig) {
367
623
 
368
624
  // src/backend/routes/gateway.ts
369
625
  var import_express2 = require("express");
370
- var import_crypto = __toESM(require("crypto"));
626
+ var import_crypto2 = __toESM(require("crypto"));
371
627
  function createGatewayRoutes(options) {
372
628
  const { rateLimiterService, analyticsService, config } = options;
373
629
  const router = (0, import_express2.Router)();
@@ -410,7 +666,7 @@ function createGatewayRoutes(options) {
410
666
  }
411
667
  const newKey = {
412
668
  id: `key_${String(config.apiKeys.keys.length + 1).padStart(3, "0")}`,
413
- key: `gw_live_${import_crypto.default.randomBytes(16).toString("hex")}`,
669
+ key: `gw_live_${import_crypto2.default.randomBytes(16).toString("hex")}`,
414
670
  name,
415
671
  tier: tier || "free",
416
672
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -437,15 +693,80 @@ function createGatewayRoutes(options) {
437
693
  });
438
694
  return router;
439
695
  }
696
+
697
+ // src/backend/routes/deviceAuth.ts
698
+ var import_express3 = require("express");
699
+ function createDeviceAuthRoutes(options) {
700
+ const { deviceRegistry } = options;
701
+ const router = (0, import_express3.Router)();
702
+ router.post("/register", (req, res) => {
703
+ const { browserId } = req.body;
704
+ if (!browserId || typeof browserId !== "string") {
705
+ res.status(400).json({ error: "browserId is required" });
706
+ return;
707
+ }
708
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
709
+ if (!uuidRegex.test(browserId)) {
710
+ res.status(400).json({ error: "browserId must be a valid UUID" });
711
+ return;
712
+ }
713
+ const ip = req.ip || req.socket.remoteAddress || "unknown";
714
+ const userAgent = req.headers["user-agent"] || "unknown";
715
+ const result = deviceRegistry.registerDevice(browserId, ip, userAgent);
716
+ if (!result.success) {
717
+ res.status(result.status).json({ error: result.error });
718
+ return;
719
+ }
720
+ res.status(201).json({
721
+ browserId: result.device.browserId,
722
+ sharedSecret: result.device.sharedSecret,
723
+ expiresAt: result.device.expiresAt
724
+ });
725
+ });
726
+ router.get("/status/:browserId", (req, res) => {
727
+ const browserId = req.params.browserId;
728
+ const device = deviceRegistry.getDevice(browserId);
729
+ if (!device) {
730
+ res.status(404).json({ registered: false });
731
+ return;
732
+ }
733
+ res.json({
734
+ registered: true,
735
+ browserId: device.browserId,
736
+ expiresAt: device.expiresAt,
737
+ registeredAt: device.registeredAt
738
+ });
739
+ });
740
+ router.delete("/:browserId", (req, res) => {
741
+ const browserId = req.params.browserId;
742
+ const revoked = deviceRegistry.revokeDevice(browserId);
743
+ if (!revoked) {
744
+ res.status(404).json({ error: "Device not found" });
745
+ return;
746
+ }
747
+ res.json({ message: "Device revoked", browserId });
748
+ });
749
+ router.get("/stats", (_req, res) => {
750
+ res.json(deviceRegistry.getStats());
751
+ });
752
+ return router;
753
+ }
440
754
  // Annotate the CommonJS export names for ESM import in node:
441
755
  0 && (module.exports = {
442
756
  AnalyticsService,
757
+ DeviceRegistryService,
443
758
  RateLimiterService,
444
759
  createApiKeyAuth,
760
+ createDeviceAuthRoutes,
445
761
  createGatewayMiddleware,
446
762
  createGatewayRoutes,
447
763
  createIpFilter,
448
764
  createRateLimiter,
449
- createRequestLogger
765
+ createRequestLogger,
766
+ formatKey,
767
+ generateSecret,
768
+ generateTOTP,
769
+ parseKey,
770
+ validateTOTP
450
771
  });
451
772
  //# sourceMappingURL=index.js.map