integrate-sdk 0.9.42-dev.0 → 0.9.43-dev.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/dist/adapters/index.js +221 -2
- package/dist/adapters/solid-start.js +221 -2
- package/dist/adapters/svelte-kit.js +221 -2
- package/dist/index.js +225 -2
- package/dist/server.js +251 -3
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/oauth/manager.d.ts +44 -0
- package/dist/src/oauth/manager.d.ts.map +1 -1
- package/dist/src/oauth/refresh.d.ts +124 -0
- package/dist/src/oauth/refresh.d.ts.map +1 -0
- package/dist/src/server.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/adapters/index.js
CHANGED
|
@@ -2556,6 +2556,148 @@ class IndexedDBStorage {
|
|
|
2556
2556
|
|
|
2557
2557
|
// ../oauth/manager.ts
|
|
2558
2558
|
init_email_fetcher();
|
|
2559
|
+
|
|
2560
|
+
// ../oauth/refresh.ts
|
|
2561
|
+
class RefreshRejectedError extends Error {
|
|
2562
|
+
provider;
|
|
2563
|
+
constructor(provider, message) {
|
|
2564
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
2565
|
+
this.name = "RefreshRejectedError";
|
|
2566
|
+
this.provider = provider;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
class RefreshTransientError extends Error {
|
|
2571
|
+
provider;
|
|
2572
|
+
constructor(provider, message) {
|
|
2573
|
+
super(message);
|
|
2574
|
+
this.name = "RefreshTransientError";
|
|
2575
|
+
this.provider = provider;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
2579
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
2580
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
2581
|
+
return false;
|
|
2582
|
+
}
|
|
2583
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
2584
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
2588
|
+
}
|
|
2589
|
+
async function refreshViaMcp(opts) {
|
|
2590
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
2591
|
+
const body = {
|
|
2592
|
+
provider: opts.provider,
|
|
2593
|
+
refresh_token: opts.refreshToken,
|
|
2594
|
+
client_id: opts.clientId
|
|
2595
|
+
};
|
|
2596
|
+
if (opts.clientSecret) {
|
|
2597
|
+
body.client_secret = opts.clientSecret;
|
|
2598
|
+
}
|
|
2599
|
+
if (opts.subdomain) {
|
|
2600
|
+
body.subdomain = opts.subdomain;
|
|
2601
|
+
}
|
|
2602
|
+
if (opts.extraConfig) {
|
|
2603
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
2604
|
+
if (body[key] === undefined) {
|
|
2605
|
+
body[key] = value;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
const headers = { "Content-Type": "application/json" };
|
|
2610
|
+
if (opts.apiKey) {
|
|
2611
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
2612
|
+
}
|
|
2613
|
+
let response;
|
|
2614
|
+
try {
|
|
2615
|
+
response = await fetch(url.toString(), {
|
|
2616
|
+
method: "POST",
|
|
2617
|
+
headers,
|
|
2618
|
+
body: JSON.stringify(body),
|
|
2619
|
+
signal: opts.signal
|
|
2620
|
+
});
|
|
2621
|
+
} catch (err) {
|
|
2622
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
2623
|
+
}
|
|
2624
|
+
if (response.status === 401) {
|
|
2625
|
+
let parsed = {};
|
|
2626
|
+
try {
|
|
2627
|
+
parsed = await response.json();
|
|
2628
|
+
} catch {}
|
|
2629
|
+
if (parsed.error === "invalid_grant") {
|
|
2630
|
+
throw new RefreshRejectedError(opts.provider);
|
|
2631
|
+
}
|
|
2632
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
2633
|
+
}
|
|
2634
|
+
if (!response.ok) {
|
|
2635
|
+
let text = "";
|
|
2636
|
+
try {
|
|
2637
|
+
text = await response.text();
|
|
2638
|
+
} catch {}
|
|
2639
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
2640
|
+
}
|
|
2641
|
+
let result;
|
|
2642
|
+
try {
|
|
2643
|
+
result = await response.json();
|
|
2644
|
+
} catch (err) {
|
|
2645
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
2646
|
+
}
|
|
2647
|
+
if (!result.accessToken) {
|
|
2648
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
2649
|
+
}
|
|
2650
|
+
if (!result.refreshToken) {
|
|
2651
|
+
result.refreshToken = opts.refreshToken;
|
|
2652
|
+
}
|
|
2653
|
+
return result;
|
|
2654
|
+
}
|
|
2655
|
+
async function resolveAccessToken(opts) {
|
|
2656
|
+
const { provider, currentTokens, force = false } = opts;
|
|
2657
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
2658
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
2659
|
+
return currentTokens.accessToken;
|
|
2660
|
+
}
|
|
2661
|
+
try {
|
|
2662
|
+
const refreshed = await refreshViaMcp({
|
|
2663
|
+
provider,
|
|
2664
|
+
refreshToken: currentTokens.refreshToken,
|
|
2665
|
+
clientId: opts.providerOAuth.clientId,
|
|
2666
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
2667
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
2668
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
2669
|
+
serverUrl: opts.serverUrl,
|
|
2670
|
+
apiKey: opts.apiKey
|
|
2671
|
+
});
|
|
2672
|
+
const updated = {
|
|
2673
|
+
...currentTokens,
|
|
2674
|
+
accessToken: refreshed.accessToken,
|
|
2675
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
2676
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
2677
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
2678
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
2679
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
2680
|
+
};
|
|
2681
|
+
if (opts.setProviderToken) {
|
|
2682
|
+
try {
|
|
2683
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
2684
|
+
} catch {}
|
|
2685
|
+
}
|
|
2686
|
+
return updated.accessToken;
|
|
2687
|
+
} catch (err) {
|
|
2688
|
+
if (err instanceof RefreshRejectedError) {
|
|
2689
|
+
if (opts.setProviderToken) {
|
|
2690
|
+
try {
|
|
2691
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
2692
|
+
} catch {}
|
|
2693
|
+
}
|
|
2694
|
+
throw err;
|
|
2695
|
+
}
|
|
2696
|
+
return currentTokens.accessToken;
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// ../oauth/manager.ts
|
|
2559
2701
|
init_logger();
|
|
2560
2702
|
var logger6 = createLogger("OAuth");
|
|
2561
2703
|
|
|
@@ -2571,7 +2713,10 @@ class OAuthManager {
|
|
|
2571
2713
|
removeTokenCallback;
|
|
2572
2714
|
indexedDBStorage;
|
|
2573
2715
|
skipLocalStorage = false;
|
|
2574
|
-
|
|
2716
|
+
providerOAuth = {};
|
|
2717
|
+
mcpServerUrl;
|
|
2718
|
+
mcpApiKey;
|
|
2719
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
2575
2720
|
this.oauthApiBase = oauthApiBase;
|
|
2576
2721
|
this.apiBaseUrl = apiBaseUrl;
|
|
2577
2722
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -2583,6 +2728,9 @@ class OAuthManager {
|
|
|
2583
2728
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
2584
2729
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
2585
2730
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
2731
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
2732
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
2733
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
2586
2734
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
2587
2735
|
this.cleanupExpiredPendingAuths();
|
|
2588
2736
|
}
|
|
@@ -2792,7 +2940,9 @@ class OAuthManager {
|
|
|
2792
2940
|
try {
|
|
2793
2941
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
2794
2942
|
if (tokenData) {
|
|
2795
|
-
this.
|
|
2943
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
2944
|
+
this.providerTokens.set(provider, refreshed);
|
|
2945
|
+
return refreshed;
|
|
2796
2946
|
}
|
|
2797
2947
|
return tokenData;
|
|
2798
2948
|
} catch (error) {
|
|
@@ -2832,6 +2982,59 @@ class OAuthManager {
|
|
|
2832
2982
|
}
|
|
2833
2983
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
2834
2984
|
}
|
|
2985
|
+
configureTokenRefresh(refreshConfig) {
|
|
2986
|
+
if (refreshConfig.providers) {
|
|
2987
|
+
this.providerOAuth = refreshConfig.providers;
|
|
2988
|
+
}
|
|
2989
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
2990
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
2991
|
+
}
|
|
2992
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
2993
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
2997
|
+
const credentials = this.providerOAuth[provider];
|
|
2998
|
+
const serverUrl = this.mcpServerUrl;
|
|
2999
|
+
if (!credentials || !serverUrl) {
|
|
3000
|
+
return tokenData;
|
|
3001
|
+
}
|
|
3002
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
3003
|
+
return tokenData;
|
|
3004
|
+
}
|
|
3005
|
+
try {
|
|
3006
|
+
const newAccessToken = await resolveAccessToken({
|
|
3007
|
+
provider,
|
|
3008
|
+
currentTokens: tokenData,
|
|
3009
|
+
providerOAuth: {
|
|
3010
|
+
clientId: credentials.clientId,
|
|
3011
|
+
clientSecret: credentials.clientSecret,
|
|
3012
|
+
subdomain: credentials.config?.subdomain
|
|
3013
|
+
},
|
|
3014
|
+
serverUrl,
|
|
3015
|
+
apiKey: this.mcpApiKey,
|
|
3016
|
+
setProviderToken: this.setTokenCallback,
|
|
3017
|
+
context
|
|
3018
|
+
});
|
|
3019
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
3020
|
+
return tokenData;
|
|
3021
|
+
}
|
|
3022
|
+
if (this.getTokenCallback) {
|
|
3023
|
+
try {
|
|
3024
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
3025
|
+
if (reloaded) {
|
|
3026
|
+
return reloaded;
|
|
3027
|
+
}
|
|
3028
|
+
} catch {}
|
|
3029
|
+
}
|
|
3030
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
3031
|
+
} catch (err) {
|
|
3032
|
+
if (err instanceof RefreshRejectedError) {
|
|
3033
|
+
throw err;
|
|
3034
|
+
}
|
|
3035
|
+
return tokenData;
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
2835
3038
|
clearProviderToken(provider) {
|
|
2836
3039
|
this.providerTokens.delete(provider);
|
|
2837
3040
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -3288,10 +3491,26 @@ class MCPClientBase {
|
|
|
3288
3491
|
};
|
|
3289
3492
|
this.onReauthRequired = config.onReauthRequired;
|
|
3290
3493
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
3494
|
+
const refreshProviders = {};
|
|
3495
|
+
for (const integration of this.integrations) {
|
|
3496
|
+
const oauth = integration?.oauth;
|
|
3497
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
3498
|
+
refreshProviders[oauth.provider] = {
|
|
3499
|
+
clientId: oauth.clientId,
|
|
3500
|
+
clientSecret: oauth.clientSecret,
|
|
3501
|
+
config: oauth.config
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
const mcpServerUrl = config.serverUrl;
|
|
3291
3506
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
3292
3507
|
getProviderToken: config.getProviderToken,
|
|
3293
3508
|
setProviderToken: config.setProviderToken,
|
|
3294
3509
|
removeProviderToken: config.removeProviderToken
|
|
3510
|
+
}, {
|
|
3511
|
+
providers: refreshProviders,
|
|
3512
|
+
mcpServerUrl,
|
|
3513
|
+
apiKey: config.apiKey
|
|
3295
3514
|
});
|
|
3296
3515
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
3297
3516
|
for (const integration of this.integrations) {
|
|
@@ -2556,6 +2556,148 @@ class IndexedDBStorage {
|
|
|
2556
2556
|
|
|
2557
2557
|
// ../oauth/manager.ts
|
|
2558
2558
|
init_email_fetcher();
|
|
2559
|
+
|
|
2560
|
+
// ../oauth/refresh.ts
|
|
2561
|
+
class RefreshRejectedError extends Error {
|
|
2562
|
+
provider;
|
|
2563
|
+
constructor(provider, message) {
|
|
2564
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
2565
|
+
this.name = "RefreshRejectedError";
|
|
2566
|
+
this.provider = provider;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
class RefreshTransientError extends Error {
|
|
2571
|
+
provider;
|
|
2572
|
+
constructor(provider, message) {
|
|
2573
|
+
super(message);
|
|
2574
|
+
this.name = "RefreshTransientError";
|
|
2575
|
+
this.provider = provider;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
2579
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
2580
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
2581
|
+
return false;
|
|
2582
|
+
}
|
|
2583
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
2584
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
2588
|
+
}
|
|
2589
|
+
async function refreshViaMcp(opts) {
|
|
2590
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
2591
|
+
const body = {
|
|
2592
|
+
provider: opts.provider,
|
|
2593
|
+
refresh_token: opts.refreshToken,
|
|
2594
|
+
client_id: opts.clientId
|
|
2595
|
+
};
|
|
2596
|
+
if (opts.clientSecret) {
|
|
2597
|
+
body.client_secret = opts.clientSecret;
|
|
2598
|
+
}
|
|
2599
|
+
if (opts.subdomain) {
|
|
2600
|
+
body.subdomain = opts.subdomain;
|
|
2601
|
+
}
|
|
2602
|
+
if (opts.extraConfig) {
|
|
2603
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
2604
|
+
if (body[key] === undefined) {
|
|
2605
|
+
body[key] = value;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
const headers = { "Content-Type": "application/json" };
|
|
2610
|
+
if (opts.apiKey) {
|
|
2611
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
2612
|
+
}
|
|
2613
|
+
let response;
|
|
2614
|
+
try {
|
|
2615
|
+
response = await fetch(url.toString(), {
|
|
2616
|
+
method: "POST",
|
|
2617
|
+
headers,
|
|
2618
|
+
body: JSON.stringify(body),
|
|
2619
|
+
signal: opts.signal
|
|
2620
|
+
});
|
|
2621
|
+
} catch (err) {
|
|
2622
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
2623
|
+
}
|
|
2624
|
+
if (response.status === 401) {
|
|
2625
|
+
let parsed = {};
|
|
2626
|
+
try {
|
|
2627
|
+
parsed = await response.json();
|
|
2628
|
+
} catch {}
|
|
2629
|
+
if (parsed.error === "invalid_grant") {
|
|
2630
|
+
throw new RefreshRejectedError(opts.provider);
|
|
2631
|
+
}
|
|
2632
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
2633
|
+
}
|
|
2634
|
+
if (!response.ok) {
|
|
2635
|
+
let text = "";
|
|
2636
|
+
try {
|
|
2637
|
+
text = await response.text();
|
|
2638
|
+
} catch {}
|
|
2639
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
2640
|
+
}
|
|
2641
|
+
let result;
|
|
2642
|
+
try {
|
|
2643
|
+
result = await response.json();
|
|
2644
|
+
} catch (err) {
|
|
2645
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
2646
|
+
}
|
|
2647
|
+
if (!result.accessToken) {
|
|
2648
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
2649
|
+
}
|
|
2650
|
+
if (!result.refreshToken) {
|
|
2651
|
+
result.refreshToken = opts.refreshToken;
|
|
2652
|
+
}
|
|
2653
|
+
return result;
|
|
2654
|
+
}
|
|
2655
|
+
async function resolveAccessToken(opts) {
|
|
2656
|
+
const { provider, currentTokens, force = false } = opts;
|
|
2657
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
2658
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
2659
|
+
return currentTokens.accessToken;
|
|
2660
|
+
}
|
|
2661
|
+
try {
|
|
2662
|
+
const refreshed = await refreshViaMcp({
|
|
2663
|
+
provider,
|
|
2664
|
+
refreshToken: currentTokens.refreshToken,
|
|
2665
|
+
clientId: opts.providerOAuth.clientId,
|
|
2666
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
2667
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
2668
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
2669
|
+
serverUrl: opts.serverUrl,
|
|
2670
|
+
apiKey: opts.apiKey
|
|
2671
|
+
});
|
|
2672
|
+
const updated = {
|
|
2673
|
+
...currentTokens,
|
|
2674
|
+
accessToken: refreshed.accessToken,
|
|
2675
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
2676
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
2677
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
2678
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
2679
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
2680
|
+
};
|
|
2681
|
+
if (opts.setProviderToken) {
|
|
2682
|
+
try {
|
|
2683
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
2684
|
+
} catch {}
|
|
2685
|
+
}
|
|
2686
|
+
return updated.accessToken;
|
|
2687
|
+
} catch (err) {
|
|
2688
|
+
if (err instanceof RefreshRejectedError) {
|
|
2689
|
+
if (opts.setProviderToken) {
|
|
2690
|
+
try {
|
|
2691
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
2692
|
+
} catch {}
|
|
2693
|
+
}
|
|
2694
|
+
throw err;
|
|
2695
|
+
}
|
|
2696
|
+
return currentTokens.accessToken;
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// ../oauth/manager.ts
|
|
2559
2701
|
init_logger();
|
|
2560
2702
|
var logger6 = createLogger("OAuth");
|
|
2561
2703
|
|
|
@@ -2571,7 +2713,10 @@ class OAuthManager {
|
|
|
2571
2713
|
removeTokenCallback;
|
|
2572
2714
|
indexedDBStorage;
|
|
2573
2715
|
skipLocalStorage = false;
|
|
2574
|
-
|
|
2716
|
+
providerOAuth = {};
|
|
2717
|
+
mcpServerUrl;
|
|
2718
|
+
mcpApiKey;
|
|
2719
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
2575
2720
|
this.oauthApiBase = oauthApiBase;
|
|
2576
2721
|
this.apiBaseUrl = apiBaseUrl;
|
|
2577
2722
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -2583,6 +2728,9 @@ class OAuthManager {
|
|
|
2583
2728
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
2584
2729
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
2585
2730
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
2731
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
2732
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
2733
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
2586
2734
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
2587
2735
|
this.cleanupExpiredPendingAuths();
|
|
2588
2736
|
}
|
|
@@ -2792,7 +2940,9 @@ class OAuthManager {
|
|
|
2792
2940
|
try {
|
|
2793
2941
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
2794
2942
|
if (tokenData) {
|
|
2795
|
-
this.
|
|
2943
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
2944
|
+
this.providerTokens.set(provider, refreshed);
|
|
2945
|
+
return refreshed;
|
|
2796
2946
|
}
|
|
2797
2947
|
return tokenData;
|
|
2798
2948
|
} catch (error) {
|
|
@@ -2832,6 +2982,59 @@ class OAuthManager {
|
|
|
2832
2982
|
}
|
|
2833
2983
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
2834
2984
|
}
|
|
2985
|
+
configureTokenRefresh(refreshConfig) {
|
|
2986
|
+
if (refreshConfig.providers) {
|
|
2987
|
+
this.providerOAuth = refreshConfig.providers;
|
|
2988
|
+
}
|
|
2989
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
2990
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
2991
|
+
}
|
|
2992
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
2993
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
2997
|
+
const credentials = this.providerOAuth[provider];
|
|
2998
|
+
const serverUrl = this.mcpServerUrl;
|
|
2999
|
+
if (!credentials || !serverUrl) {
|
|
3000
|
+
return tokenData;
|
|
3001
|
+
}
|
|
3002
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
3003
|
+
return tokenData;
|
|
3004
|
+
}
|
|
3005
|
+
try {
|
|
3006
|
+
const newAccessToken = await resolveAccessToken({
|
|
3007
|
+
provider,
|
|
3008
|
+
currentTokens: tokenData,
|
|
3009
|
+
providerOAuth: {
|
|
3010
|
+
clientId: credentials.clientId,
|
|
3011
|
+
clientSecret: credentials.clientSecret,
|
|
3012
|
+
subdomain: credentials.config?.subdomain
|
|
3013
|
+
},
|
|
3014
|
+
serverUrl,
|
|
3015
|
+
apiKey: this.mcpApiKey,
|
|
3016
|
+
setProviderToken: this.setTokenCallback,
|
|
3017
|
+
context
|
|
3018
|
+
});
|
|
3019
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
3020
|
+
return tokenData;
|
|
3021
|
+
}
|
|
3022
|
+
if (this.getTokenCallback) {
|
|
3023
|
+
try {
|
|
3024
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
3025
|
+
if (reloaded) {
|
|
3026
|
+
return reloaded;
|
|
3027
|
+
}
|
|
3028
|
+
} catch {}
|
|
3029
|
+
}
|
|
3030
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
3031
|
+
} catch (err) {
|
|
3032
|
+
if (err instanceof RefreshRejectedError) {
|
|
3033
|
+
throw err;
|
|
3034
|
+
}
|
|
3035
|
+
return tokenData;
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
2835
3038
|
clearProviderToken(provider) {
|
|
2836
3039
|
this.providerTokens.delete(provider);
|
|
2837
3040
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -3288,10 +3491,26 @@ class MCPClientBase {
|
|
|
3288
3491
|
};
|
|
3289
3492
|
this.onReauthRequired = config.onReauthRequired;
|
|
3290
3493
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
3494
|
+
const refreshProviders = {};
|
|
3495
|
+
for (const integration of this.integrations) {
|
|
3496
|
+
const oauth = integration?.oauth;
|
|
3497
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
3498
|
+
refreshProviders[oauth.provider] = {
|
|
3499
|
+
clientId: oauth.clientId,
|
|
3500
|
+
clientSecret: oauth.clientSecret,
|
|
3501
|
+
config: oauth.config
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
const mcpServerUrl = config.serverUrl;
|
|
3291
3506
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
3292
3507
|
getProviderToken: config.getProviderToken,
|
|
3293
3508
|
setProviderToken: config.setProviderToken,
|
|
3294
3509
|
removeProviderToken: config.removeProviderToken
|
|
3510
|
+
}, {
|
|
3511
|
+
providers: refreshProviders,
|
|
3512
|
+
mcpServerUrl,
|
|
3513
|
+
apiKey: config.apiKey
|
|
3295
3514
|
});
|
|
3296
3515
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
3297
3516
|
for (const integration of this.integrations) {
|