@lanonasis/oauth-client 1.2.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -126,5 +126,24 @@ const hashed = await apiKeys.getApiKey(); // returns sha256 hex digest
126
126
  - `README.md`
127
127
  - `LICENSE`
128
128
 
129
+ ## Auth-Gateway Compatibility
130
+
131
+ This SDK implements client-side equivalents of the auth-gateway's authentication methods:
132
+
133
+ | Auth-Gateway Endpoint | SDK Class | Use Case |
134
+ |-----------------------|-----------|----------|
135
+ | `POST /oauth/device` | `TerminalOAuthFlow` | CLI/terminal apps |
136
+ | `GET /oauth/authorize` | `DesktopOAuthFlow` | Desktop apps (Electron) |
137
+ | `POST /v1/auth/otp/*` | `MagicLinkFlow` | Mobile/passwordless |
138
+ | `X-API-Key` header | `APIKeyFlow` | Server-to-server |
139
+ | `MCPClient` | Combined | Auto-detects best method |
140
+
141
+ ### Methods NOT Exposed (Security)
142
+ - Email/Password: Server-rendered form only
143
+ - Admin Bypass: Internal emergency access
144
+ - Supabase OAuth 2.1: Internal Supabase integration
145
+
146
+ See [auth-gateway/AUTHENTICATION-METHODS.md](../../apps/onasis-core/services/auth-gateway/AUTHENTICATION-METHODS.md) for complete server-side documentation.
147
+
129
148
  ## License
130
149
  MIT © Lan Onasis
package/dist/browser.cjs CHANGED
@@ -433,7 +433,7 @@ var APIKeyFlow = class extends BaseOAuthFlow {
433
433
  */
434
434
  async validateAPIKey() {
435
435
  try {
436
- const response = await (0, import_cross_fetch2.default)(`${this.config.authBaseUrl}/api/v1/health`, {
436
+ const response = await (0, import_cross_fetch2.default)(`${this.authBaseUrl}/api/v1/health`, {
437
437
  headers: {
438
438
  "x-api-key": this.apiKey
439
439
  }
package/dist/browser.mjs CHANGED
@@ -399,7 +399,7 @@ var APIKeyFlow = class extends BaseOAuthFlow {
399
399
  */
400
400
  async validateAPIKey() {
401
401
  try {
402
- const response = await fetch2(`${this.config.authBaseUrl}/api/v1/health`, {
402
+ const response = await fetch2(`${this.authBaseUrl}/api/v1/health`, {
403
403
  headers: {
404
404
  "x-api-key": this.apiKey
405
405
  }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * React-specific types for SSO authentication
3
+ * @module @lanonasis/oauth-client/react
4
+ */
5
+ /**
6
+ * SSO User information from the lanonasis_user cookie
7
+ */
8
+ interface SSOUser {
9
+ id: string;
10
+ email: string;
11
+ role: string;
12
+ name?: string;
13
+ avatar_url?: string;
14
+ }
15
+ /**
16
+ * SSO authentication state
17
+ */
18
+ interface SSOState {
19
+ /** Whether the user is authenticated */
20
+ isAuthenticated: boolean;
21
+ /** Whether the auth state is being determined */
22
+ isLoading: boolean;
23
+ /** User information if authenticated */
24
+ user: SSOUser | null;
25
+ /** Error message if auth check failed */
26
+ error: string | null;
27
+ }
28
+ /**
29
+ * Configuration for the SSO hook
30
+ */
31
+ interface SSOConfig {
32
+ /** Auth gateway URL (default: https://auth.lanonasis.com) */
33
+ authGatewayUrl?: string;
34
+ /** Cookie domain (default: .lanonasis.com) */
35
+ cookieDomain?: string;
36
+ /** Callback when auth state changes */
37
+ onAuthChange?: (state: SSOState) => void;
38
+ /** Polling interval in ms for cookie changes (default: 30000) */
39
+ pollInterval?: number;
40
+ }
41
+ /**
42
+ * Configuration for Supabase-to-cookie sync
43
+ */
44
+ interface SSOSyncConfig {
45
+ /** Auth gateway URL (default: https://auth.lanonasis.com) */
46
+ authGatewayUrl?: string;
47
+ /** Project scope for multi-tenant auth */
48
+ projectScope?: string;
49
+ /** Callback when sync completes */
50
+ onSyncComplete?: (success: boolean) => void;
51
+ }
52
+ /**
53
+ * Supabase session interface (minimal for what we need)
54
+ */
55
+ interface SupabaseSession {
56
+ access_token: string;
57
+ refresh_token?: string;
58
+ }
59
+ /**
60
+ * Return type for useSSO hook
61
+ */
62
+ interface UseSSOReturn extends SSOState {
63
+ /** Manually refresh auth state */
64
+ refresh: () => void;
65
+ /** Logout and redirect to auth gateway */
66
+ logout: (returnTo?: string) => void;
67
+ /** Get login URL with optional return URL */
68
+ getLoginUrl: (returnTo?: string) => string;
69
+ }
70
+ /**
71
+ * Return type for useSSOSync hook
72
+ */
73
+ interface UseSSOSyncReturn {
74
+ /** Manually trigger sync */
75
+ sync: () => Promise<boolean>;
76
+ /** Sync with gateway directly */
77
+ syncWithGateway: (session: SupabaseSession) => Promise<boolean>;
78
+ }
79
+
80
+ /**
81
+ * Cookie constants and utilities shared between browser and server
82
+ * @module @lanonasis/oauth-client/cookies
83
+ */
84
+ /**
85
+ * Cookie names used by Lan Onasis auth system
86
+ */
87
+ declare const COOKIE_NAMES: {
88
+ /** HttpOnly JWT session token */
89
+ readonly SESSION: "lanonasis_session";
90
+ /** Readable user metadata (JSON) */
91
+ readonly USER: "lanonasis_user";
92
+ };
93
+ /**
94
+ * Default cookie domain for cross-subdomain SSO
95
+ */
96
+ declare const DEFAULT_COOKIE_DOMAIN = ".lanonasis.com";
97
+ /**
98
+ * Default auth gateway URL
99
+ */
100
+ declare const DEFAULT_AUTH_GATEWAY = "https://auth.lanonasis.com";
101
+ /**
102
+ * Default polling interval for cookie changes (30 seconds)
103
+ */
104
+ declare const DEFAULT_POLL_INTERVAL = 30000;
105
+ /**
106
+ * Default project scope for multi-tenant auth
107
+ */
108
+ declare const DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
109
+
110
+ export { COOKIE_NAMES as C, DEFAULT_AUTH_GATEWAY as D, type SSOConfig as S, type UseSSOReturn as U, type SupabaseSession as a, type SSOSyncConfig as b, type UseSSOSyncReturn as c, type SSOUser as d, type SSOState as e, DEFAULT_COOKIE_DOMAIN as f, DEFAULT_POLL_INTERVAL as g, DEFAULT_PROJECT_SCOPE as h };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * React-specific types for SSO authentication
3
+ * @module @lanonasis/oauth-client/react
4
+ */
5
+ /**
6
+ * SSO User information from the lanonasis_user cookie
7
+ */
8
+ interface SSOUser {
9
+ id: string;
10
+ email: string;
11
+ role: string;
12
+ name?: string;
13
+ avatar_url?: string;
14
+ }
15
+ /**
16
+ * SSO authentication state
17
+ */
18
+ interface SSOState {
19
+ /** Whether the user is authenticated */
20
+ isAuthenticated: boolean;
21
+ /** Whether the auth state is being determined */
22
+ isLoading: boolean;
23
+ /** User information if authenticated */
24
+ user: SSOUser | null;
25
+ /** Error message if auth check failed */
26
+ error: string | null;
27
+ }
28
+ /**
29
+ * Configuration for the SSO hook
30
+ */
31
+ interface SSOConfig {
32
+ /** Auth gateway URL (default: https://auth.lanonasis.com) */
33
+ authGatewayUrl?: string;
34
+ /** Cookie domain (default: .lanonasis.com) */
35
+ cookieDomain?: string;
36
+ /** Callback when auth state changes */
37
+ onAuthChange?: (state: SSOState) => void;
38
+ /** Polling interval in ms for cookie changes (default: 30000) */
39
+ pollInterval?: number;
40
+ }
41
+ /**
42
+ * Configuration for Supabase-to-cookie sync
43
+ */
44
+ interface SSOSyncConfig {
45
+ /** Auth gateway URL (default: https://auth.lanonasis.com) */
46
+ authGatewayUrl?: string;
47
+ /** Project scope for multi-tenant auth */
48
+ projectScope?: string;
49
+ /** Callback when sync completes */
50
+ onSyncComplete?: (success: boolean) => void;
51
+ }
52
+ /**
53
+ * Supabase session interface (minimal for what we need)
54
+ */
55
+ interface SupabaseSession {
56
+ access_token: string;
57
+ refresh_token?: string;
58
+ }
59
+ /**
60
+ * Return type for useSSO hook
61
+ */
62
+ interface UseSSOReturn extends SSOState {
63
+ /** Manually refresh auth state */
64
+ refresh: () => void;
65
+ /** Logout and redirect to auth gateway */
66
+ logout: (returnTo?: string) => void;
67
+ /** Get login URL with optional return URL */
68
+ getLoginUrl: (returnTo?: string) => string;
69
+ }
70
+ /**
71
+ * Return type for useSSOSync hook
72
+ */
73
+ interface UseSSOSyncReturn {
74
+ /** Manually trigger sync */
75
+ sync: () => Promise<boolean>;
76
+ /** Sync with gateway directly */
77
+ syncWithGateway: (session: SupabaseSession) => Promise<boolean>;
78
+ }
79
+
80
+ /**
81
+ * Cookie constants and utilities shared between browser and server
82
+ * @module @lanonasis/oauth-client/cookies
83
+ */
84
+ /**
85
+ * Cookie names used by Lan Onasis auth system
86
+ */
87
+ declare const COOKIE_NAMES: {
88
+ /** HttpOnly JWT session token */
89
+ readonly SESSION: "lanonasis_session";
90
+ /** Readable user metadata (JSON) */
91
+ readonly USER: "lanonasis_user";
92
+ };
93
+ /**
94
+ * Default cookie domain for cross-subdomain SSO
95
+ */
96
+ declare const DEFAULT_COOKIE_DOMAIN = ".lanonasis.com";
97
+ /**
98
+ * Default auth gateway URL
99
+ */
100
+ declare const DEFAULT_AUTH_GATEWAY = "https://auth.lanonasis.com";
101
+ /**
102
+ * Default polling interval for cookie changes (30 seconds)
103
+ */
104
+ declare const DEFAULT_POLL_INTERVAL = 30000;
105
+ /**
106
+ * Default project scope for multi-tenant auth
107
+ */
108
+ declare const DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
109
+
110
+ export { COOKIE_NAMES as C, DEFAULT_AUTH_GATEWAY as D, type SSOConfig as S, type UseSSOReturn as U, type SupabaseSession as a, type SSOSyncConfig as b, type UseSSOSyncReturn as c, type SSOUser as d, type SSOState as e, DEFAULT_COOKIE_DOMAIN as f, DEFAULT_POLL_INTERVAL as g, DEFAULT_PROJECT_SCOPE as h };
package/dist/index.cjs CHANGED
@@ -30,12 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ APIKeyFlow: () => APIKeyFlow,
33
34
  ApiKeyStorage: () => ApiKeyStorage,
34
35
  ApiKeyStorageWeb: () => ApiKeyStorageWeb,
35
36
  AuthGatewayClient: () => AuthGatewayClient,
36
37
  BaseOAuthFlow: () => BaseOAuthFlow,
37
38
  DesktopOAuthFlow: () => DesktopOAuthFlow,
38
39
  MCPClient: () => MCPClient,
40
+ MagicLinkFlow: () => MagicLinkFlow,
39
41
  TerminalOAuthFlow: () => TerminalOAuthFlow,
40
42
  TokenStorage: () => TokenStorage,
41
43
  TokenStorageWeb: () => TokenStorageWeb
@@ -368,6 +370,272 @@ var DesktopOAuthFlow = class extends BaseOAuthFlow {
368
370
  }
369
371
  };
370
372
 
373
+ // src/flows/magic-link-flow.ts
374
+ var import_cross_fetch3 = __toESM(require("cross-fetch"), 1);
375
+ var MagicLinkFlow = class extends BaseOAuthFlow {
376
+ constructor(config) {
377
+ super(config);
378
+ this.projectScope = config.projectScope || "lanonasis-maas";
379
+ this.platform = config.platform || "cli";
380
+ }
381
+ /**
382
+ * Main authenticate method - uses OTP code flow by default
383
+ * For interactive CLI usage, prefer using requestOTP() and verifyOTP() separately
384
+ */
385
+ async authenticate() {
386
+ throw new Error(
387
+ "MagicLinkFlow requires two-step authentication. Use requestOTP() + verifyOTP() for CLI, or requestMagicLink() for web."
388
+ );
389
+ }
390
+ // ============================================================================
391
+ // OTP Code Flow (for CLI, mobile - user enters code manually)
392
+ // ============================================================================
393
+ /**
394
+ * Request a 6-digit OTP code to be sent via email
395
+ * User will enter this code manually in the CLI
396
+ *
397
+ * @param email - User's email address
398
+ * @returns Response with success status and expiration time
399
+ */
400
+ async requestOTP(email) {
401
+ if (typeof email !== "string" || !email.trim()) {
402
+ throw new Error("Invalid email: a non-empty email address is required to request an OTP.");
403
+ }
404
+ const normalizedEmail = email.trim().toLowerCase();
405
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/otp/send`, {
406
+ method: "POST",
407
+ headers: { "Content-Type": "application/json" },
408
+ body: JSON.stringify({
409
+ email: normalizedEmail,
410
+ type: "email",
411
+ // Explicitly request 6-digit code, not magic link
412
+ platform: this.platform,
413
+ project_scope: this.projectScope
414
+ })
415
+ });
416
+ const data = await response.json();
417
+ if (!response.ok) {
418
+ throw new Error(data.message || data.error || "Failed to send OTP");
419
+ }
420
+ return data;
421
+ }
422
+ /**
423
+ * Verify the OTP code entered by the user and get tokens
424
+ *
425
+ * @param email - User's email address (must match the one used in requestOTP)
426
+ * @param code - 6-digit OTP code from email
427
+ * @returns Token response with access_token, refresh_token, etc.
428
+ */
429
+ async verifyOTP(email, code) {
430
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/otp/verify`, {
431
+ method: "POST",
432
+ headers: { "Content-Type": "application/json" },
433
+ body: JSON.stringify({
434
+ email: email.trim().toLowerCase(),
435
+ token: code.trim(),
436
+ type: "email",
437
+ platform: this.platform,
438
+ project_scope: this.projectScope
439
+ })
440
+ });
441
+ const data = await response.json();
442
+ if (!response.ok) {
443
+ throw new Error(data.message || data.error || "Invalid or expired OTP code");
444
+ }
445
+ return data;
446
+ }
447
+ /**
448
+ * Resend OTP code (rate limited)
449
+ *
450
+ * @param email - User's email address
451
+ * @returns Response with success status
452
+ */
453
+ async resendOTP(email) {
454
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/otp/resend`, {
455
+ method: "POST",
456
+ headers: { "Content-Type": "application/json" },
457
+ body: JSON.stringify({
458
+ email: email.trim().toLowerCase(),
459
+ type: "email",
460
+ platform: this.platform
461
+ })
462
+ });
463
+ const data = await response.json();
464
+ if (!response.ok) {
465
+ if (response.status === 429) {
466
+ throw new Error("Rate limited. Please wait before requesting another code.");
467
+ }
468
+ throw new Error(data.message || data.error || "Failed to resend OTP");
469
+ }
470
+ return data;
471
+ }
472
+ // ============================================================================
473
+ // Magic Link Flow (for web, desktop - user clicks link in email)
474
+ // ============================================================================
475
+ /**
476
+ * Request a magic link to be sent via email
477
+ * User will click the link which redirects to your callback URL
478
+ *
479
+ * @param email - User's email address
480
+ * @param redirectUri - URL to redirect to after clicking the magic link
481
+ * @returns Response with success status
482
+ */
483
+ async requestMagicLink(email, redirectUri) {
484
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/otp/send`, {
485
+ method: "POST",
486
+ headers: { "Content-Type": "application/json" },
487
+ body: JSON.stringify({
488
+ email: email.trim().toLowerCase(),
489
+ type: "magiclink",
490
+ redirect_uri: redirectUri,
491
+ platform: this.platform,
492
+ project_scope: this.projectScope
493
+ })
494
+ });
495
+ const data = await response.json();
496
+ if (!response.ok) {
497
+ throw new Error(data.message || data.error || "Failed to send magic link");
498
+ }
499
+ return data;
500
+ }
501
+ /**
502
+ * Alternative: Use the /v1/auth/magic-link endpoint (web-optimized)
503
+ * This endpoint provides better redirect handling for web apps
504
+ *
505
+ * @param email - User's email address
506
+ * @param redirectUri - URL to redirect to after authentication
507
+ * @param createUser - Whether to create a new user if email doesn't exist
508
+ */
509
+ async requestMagicLinkWeb(email, redirectUri, createUser = true) {
510
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/magic-link`, {
511
+ method: "POST",
512
+ headers: { "Content-Type": "application/json" },
513
+ body: JSON.stringify({
514
+ email: email.trim().toLowerCase(),
515
+ redirect_uri: redirectUri,
516
+ return_to: redirectUri,
517
+ // Alias for compatibility
518
+ project_scope: this.projectScope,
519
+ platform: "web",
520
+ create_user: createUser
521
+ })
522
+ });
523
+ const data = await response.json();
524
+ if (!response.ok) {
525
+ throw new Error(data.message || data.error || "Failed to send magic link");
526
+ }
527
+ return data;
528
+ }
529
+ /**
530
+ * Exchange magic link token for auth-gateway tokens
531
+ * Called after user clicks the magic link and is redirected to your callback
532
+ *
533
+ * @param supabaseAccessToken - Access token from Supabase (from URL hash/query)
534
+ * @param state - State parameter from the callback URL
535
+ * @returns Token response with redirect URL
536
+ */
537
+ async exchangeMagicLinkToken(supabaseAccessToken, state) {
538
+ const response = await (0, import_cross_fetch3.default)(`${this.authBaseUrl}/v1/auth/magic-link/exchange`, {
539
+ method: "POST",
540
+ headers: {
541
+ "Content-Type": "application/json",
542
+ "Authorization": `Bearer ${supabaseAccessToken}`
543
+ },
544
+ body: JSON.stringify({ state })
545
+ });
546
+ const data = await response.json();
547
+ if (!response.ok) {
548
+ throw new Error(data.message || data.error || "Magic link exchange failed");
549
+ }
550
+ return data;
551
+ }
552
+ // ============================================================================
553
+ // Utility Methods
554
+ // ============================================================================
555
+ /**
556
+ * Check if an email is valid format
557
+ */
558
+ static isValidEmail(email) {
559
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
560
+ return emailRegex.test(email.trim());
561
+ }
562
+ /**
563
+ * Check if an OTP code is valid format (6 digits).
564
+ *
565
+ * This method:
566
+ * - Trims leading and trailing whitespace from the input before validating.
567
+ * - Requires exactly 6 numeric digits (0–9); any non-numeric characters or
568
+ * incorrect length will cause it to return `false`.
569
+ *
570
+ * Note: This method expects a string value. Passing `null`, `undefined`, or
571
+ * other non-string values will result in a runtime error when `.trim()` is
572
+ * called, rather than returning `false`.
573
+ *
574
+ * @param code - The OTP code to validate.
575
+ * @returns `true` if the trimmed code consists of exactly 6 digits, otherwise `false`.
576
+ */
577
+ static isValidOTPCode(code) {
578
+ return /^\d{6}$/.test(code.trim());
579
+ }
580
+ };
581
+
582
+ // src/flows/apikey-flow.ts
583
+ var import_cross_fetch4 = __toESM(require("cross-fetch"), 1);
584
+ var APIKeyFlow = class extends BaseOAuthFlow {
585
+ constructor(apiKey, authBaseUrl = "https://mcp.lanonasis.com") {
586
+ super({
587
+ clientId: "api-key-client",
588
+ authBaseUrl
589
+ });
590
+ this.apiKey = apiKey;
591
+ }
592
+ /**
593
+ * "Authenticate" by returning the API key as a virtual token
594
+ * The API key will be used directly in request headers
595
+ */
596
+ async authenticate() {
597
+ if (!this.apiKey || !this.apiKey.startsWith("lano_") && !this.apiKey.startsWith("vx_")) {
598
+ throw new Error(
599
+ 'Invalid API key format. Must start with "lano_" or "vx_". Please regenerate your API key from the dashboard.'
600
+ );
601
+ }
602
+ if (this.apiKey.startsWith("vx_")) {
603
+ console.warn(
604
+ '\u26A0\uFE0F DEPRECATION WARNING: API keys with "vx_" prefix are deprecated and will stop working soon. Please regenerate your API key from the dashboard to get a "lano_" prefixed key. Support for "vx_" keys will be removed in a future version.'
605
+ );
606
+ }
607
+ return {
608
+ access_token: this.apiKey,
609
+ token_type: "api-key",
610
+ expires_in: 0,
611
+ // API keys don't expire
612
+ issued_at: Date.now()
613
+ };
614
+ }
615
+ /**
616
+ * API keys don't need refresh
617
+ */
618
+ async refreshToken(refreshToken) {
619
+ throw new Error("API keys do not support token refresh");
620
+ }
621
+ /**
622
+ * Optional: Validate API key by making a test request
623
+ */
624
+ async validateAPIKey() {
625
+ try {
626
+ const response = await (0, import_cross_fetch4.default)(`${this.authBaseUrl}/api/v1/health`, {
627
+ headers: {
628
+ "x-api-key": this.apiKey
629
+ }
630
+ });
631
+ return response.ok;
632
+ } catch (error) {
633
+ console.error("API key validation failed:", error);
634
+ return false;
635
+ }
636
+ }
637
+ };
638
+
371
639
  // src/storage/token-storage.ts
372
640
  var _fs = null;
373
641
  var _path = null;
@@ -1405,66 +1673,7 @@ var ApiKeyStorageWeb = class {
1405
1673
  };
1406
1674
 
1407
1675
  // src/client/mcp-client.ts
1408
- var import_cross_fetch4 = __toESM(require("cross-fetch"), 1);
1409
-
1410
- // src/flows/apikey-flow.ts
1411
- var import_cross_fetch3 = __toESM(require("cross-fetch"), 1);
1412
- var APIKeyFlow = class extends BaseOAuthFlow {
1413
- constructor(apiKey, authBaseUrl = "https://mcp.lanonasis.com") {
1414
- super({
1415
- clientId: "api-key-client",
1416
- authBaseUrl
1417
- });
1418
- this.apiKey = apiKey;
1419
- }
1420
- /**
1421
- * "Authenticate" by returning the API key as a virtual token
1422
- * The API key will be used directly in request headers
1423
- */
1424
- async authenticate() {
1425
- if (!this.apiKey || !this.apiKey.startsWith("lano_") && !this.apiKey.startsWith("vx_")) {
1426
- throw new Error(
1427
- 'Invalid API key format. Must start with "lano_" or "vx_". Please regenerate your API key from the dashboard.'
1428
- );
1429
- }
1430
- if (this.apiKey.startsWith("vx_")) {
1431
- console.warn(
1432
- '\u26A0\uFE0F DEPRECATION WARNING: API keys with "vx_" prefix are deprecated and will stop working soon. Please regenerate your API key from the dashboard to get a "lano_" prefixed key. Support for "vx_" keys will be removed in a future version.'
1433
- );
1434
- }
1435
- return {
1436
- access_token: this.apiKey,
1437
- token_type: "api-key",
1438
- expires_in: 0,
1439
- // API keys don't expire
1440
- issued_at: Date.now()
1441
- };
1442
- }
1443
- /**
1444
- * API keys don't need refresh
1445
- */
1446
- async refreshToken(refreshToken) {
1447
- throw new Error("API keys do not support token refresh");
1448
- }
1449
- /**
1450
- * Optional: Validate API key by making a test request
1451
- */
1452
- async validateAPIKey() {
1453
- try {
1454
- const response = await (0, import_cross_fetch3.default)(`${this.config.authBaseUrl}/api/v1/health`, {
1455
- headers: {
1456
- "x-api-key": this.apiKey
1457
- }
1458
- });
1459
- return response.ok;
1460
- } catch (error) {
1461
- console.error("API key validation failed:", error);
1462
- return false;
1463
- }
1464
- }
1465
- };
1466
-
1467
- // src/client/mcp-client.ts
1676
+ var import_cross_fetch5 = __toESM(require("cross-fetch"), 1);
1468
1677
  var MCPClient = class {
1469
1678
  constructor(config = {}) {
1470
1679
  // ← NEW: Track auth mode
@@ -1693,7 +1902,7 @@ var MCPClient = class {
1693
1902
  } else {
1694
1903
  headers["Authorization"] = `Bearer ${this.accessToken}`;
1695
1904
  }
1696
- const response = await (0, import_cross_fetch4.default)(`${this.config.mcpEndpoint}/api`, {
1905
+ const response = await (0, import_cross_fetch5.default)(`${this.config.mcpEndpoint}/api`, {
1697
1906
  method: "POST",
1698
1907
  headers,
1699
1908
  body: JSON.stringify({
@@ -1794,7 +2003,7 @@ var MCPClient = class {
1794
2003
  };
1795
2004
 
1796
2005
  // src/client/auth-gateway-client.ts
1797
- var import_cross_fetch5 = __toESM(require("cross-fetch"), 1);
2006
+ var import_cross_fetch6 = __toESM(require("cross-fetch"), 1);
1798
2007
  var GatewayOAuthFlow = class extends BaseOAuthFlow {
1799
2008
  async authenticate() {
1800
2009
  throw new Error("Interactive authentication is not supported in AuthGatewayClient.");
@@ -1958,7 +2167,7 @@ var AuthGatewayClient = class {
1958
2167
  return "jwt";
1959
2168
  }
1960
2169
  async requestJson(path, options) {
1961
- const response = await (0, import_cross_fetch5.default)(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
2170
+ const response = await (0, import_cross_fetch6.default)(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
1962
2171
  const text = await response.text();
1963
2172
  let data = null;
1964
2173
  if (text) {