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
|
@@ -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) {
|
package/dist/index.js
CHANGED
|
@@ -1767,6 +1767,146 @@ async function fetchNotionEmail(accessToken) {
|
|
|
1767
1767
|
}
|
|
1768
1768
|
}
|
|
1769
1769
|
|
|
1770
|
+
// src/oauth/refresh.ts
|
|
1771
|
+
class RefreshRejectedError extends Error {
|
|
1772
|
+
provider;
|
|
1773
|
+
constructor(provider, message) {
|
|
1774
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
1775
|
+
this.name = "RefreshRejectedError";
|
|
1776
|
+
this.provider = provider;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
class RefreshTransientError extends Error {
|
|
1781
|
+
provider;
|
|
1782
|
+
constructor(provider, message) {
|
|
1783
|
+
super(message);
|
|
1784
|
+
this.name = "RefreshTransientError";
|
|
1785
|
+
this.provider = provider;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
1789
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
1790
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
1791
|
+
return false;
|
|
1792
|
+
}
|
|
1793
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
1794
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
1795
|
+
return false;
|
|
1796
|
+
}
|
|
1797
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
1798
|
+
}
|
|
1799
|
+
async function refreshViaMcp(opts) {
|
|
1800
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
1801
|
+
const body = {
|
|
1802
|
+
provider: opts.provider,
|
|
1803
|
+
refresh_token: opts.refreshToken,
|
|
1804
|
+
client_id: opts.clientId
|
|
1805
|
+
};
|
|
1806
|
+
if (opts.clientSecret) {
|
|
1807
|
+
body.client_secret = opts.clientSecret;
|
|
1808
|
+
}
|
|
1809
|
+
if (opts.subdomain) {
|
|
1810
|
+
body.subdomain = opts.subdomain;
|
|
1811
|
+
}
|
|
1812
|
+
if (opts.extraConfig) {
|
|
1813
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
1814
|
+
if (body[key] === undefined) {
|
|
1815
|
+
body[key] = value;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
const headers = { "Content-Type": "application/json" };
|
|
1820
|
+
if (opts.apiKey) {
|
|
1821
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
1822
|
+
}
|
|
1823
|
+
let response;
|
|
1824
|
+
try {
|
|
1825
|
+
response = await fetch(url.toString(), {
|
|
1826
|
+
method: "POST",
|
|
1827
|
+
headers,
|
|
1828
|
+
body: JSON.stringify(body),
|
|
1829
|
+
signal: opts.signal
|
|
1830
|
+
});
|
|
1831
|
+
} catch (err) {
|
|
1832
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
1833
|
+
}
|
|
1834
|
+
if (response.status === 401) {
|
|
1835
|
+
let parsed = {};
|
|
1836
|
+
try {
|
|
1837
|
+
parsed = await response.json();
|
|
1838
|
+
} catch {}
|
|
1839
|
+
if (parsed.error === "invalid_grant") {
|
|
1840
|
+
throw new RefreshRejectedError(opts.provider);
|
|
1841
|
+
}
|
|
1842
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
1843
|
+
}
|
|
1844
|
+
if (!response.ok) {
|
|
1845
|
+
let text = "";
|
|
1846
|
+
try {
|
|
1847
|
+
text = await response.text();
|
|
1848
|
+
} catch {}
|
|
1849
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
1850
|
+
}
|
|
1851
|
+
let result;
|
|
1852
|
+
try {
|
|
1853
|
+
result = await response.json();
|
|
1854
|
+
} catch (err) {
|
|
1855
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
1856
|
+
}
|
|
1857
|
+
if (!result.accessToken) {
|
|
1858
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
1859
|
+
}
|
|
1860
|
+
if (!result.refreshToken) {
|
|
1861
|
+
result.refreshToken = opts.refreshToken;
|
|
1862
|
+
}
|
|
1863
|
+
return result;
|
|
1864
|
+
}
|
|
1865
|
+
async function resolveAccessToken(opts) {
|
|
1866
|
+
const { provider, currentTokens, force = false } = opts;
|
|
1867
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
1868
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
1869
|
+
return currentTokens.accessToken;
|
|
1870
|
+
}
|
|
1871
|
+
try {
|
|
1872
|
+
const refreshed = await refreshViaMcp({
|
|
1873
|
+
provider,
|
|
1874
|
+
refreshToken: currentTokens.refreshToken,
|
|
1875
|
+
clientId: opts.providerOAuth.clientId,
|
|
1876
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
1877
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
1878
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
1879
|
+
serverUrl: opts.serverUrl,
|
|
1880
|
+
apiKey: opts.apiKey
|
|
1881
|
+
});
|
|
1882
|
+
const updated = {
|
|
1883
|
+
...currentTokens,
|
|
1884
|
+
accessToken: refreshed.accessToken,
|
|
1885
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
1886
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
1887
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
1888
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
1889
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
1890
|
+
};
|
|
1891
|
+
if (opts.setProviderToken) {
|
|
1892
|
+
try {
|
|
1893
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
1894
|
+
} catch {}
|
|
1895
|
+
}
|
|
1896
|
+
return updated.accessToken;
|
|
1897
|
+
} catch (err) {
|
|
1898
|
+
if (err instanceof RefreshRejectedError) {
|
|
1899
|
+
if (opts.setProviderToken) {
|
|
1900
|
+
try {
|
|
1901
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
1902
|
+
} catch {}
|
|
1903
|
+
}
|
|
1904
|
+
throw err;
|
|
1905
|
+
}
|
|
1906
|
+
return currentTokens.accessToken;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1770
1910
|
// src/oauth/manager.ts
|
|
1771
1911
|
var logger4 = createLogger("OAuth");
|
|
1772
1912
|
|
|
@@ -1782,7 +1922,10 @@ class OAuthManager {
|
|
|
1782
1922
|
removeTokenCallback;
|
|
1783
1923
|
indexedDBStorage;
|
|
1784
1924
|
skipLocalStorage = false;
|
|
1785
|
-
|
|
1925
|
+
providerOAuth = {};
|
|
1926
|
+
mcpServerUrl;
|
|
1927
|
+
mcpApiKey;
|
|
1928
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
1786
1929
|
this.oauthApiBase = oauthApiBase;
|
|
1787
1930
|
this.apiBaseUrl = apiBaseUrl;
|
|
1788
1931
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -1794,6 +1937,9 @@ class OAuthManager {
|
|
|
1794
1937
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
1795
1938
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
1796
1939
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
1940
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
1941
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
1942
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
1797
1943
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
1798
1944
|
this.cleanupExpiredPendingAuths();
|
|
1799
1945
|
}
|
|
@@ -2003,7 +2149,9 @@ class OAuthManager {
|
|
|
2003
2149
|
try {
|
|
2004
2150
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
2005
2151
|
if (tokenData) {
|
|
2006
|
-
this.
|
|
2152
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
2153
|
+
this.providerTokens.set(provider, refreshed);
|
|
2154
|
+
return refreshed;
|
|
2007
2155
|
}
|
|
2008
2156
|
return tokenData;
|
|
2009
2157
|
} catch (error) {
|
|
@@ -2043,6 +2191,59 @@ class OAuthManager {
|
|
|
2043
2191
|
}
|
|
2044
2192
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
2045
2193
|
}
|
|
2194
|
+
configureTokenRefresh(refreshConfig) {
|
|
2195
|
+
if (refreshConfig.providers) {
|
|
2196
|
+
this.providerOAuth = refreshConfig.providers;
|
|
2197
|
+
}
|
|
2198
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
2199
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
2200
|
+
}
|
|
2201
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
2202
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
2206
|
+
const credentials = this.providerOAuth[provider];
|
|
2207
|
+
const serverUrl = this.mcpServerUrl;
|
|
2208
|
+
if (!credentials || !serverUrl) {
|
|
2209
|
+
return tokenData;
|
|
2210
|
+
}
|
|
2211
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
2212
|
+
return tokenData;
|
|
2213
|
+
}
|
|
2214
|
+
try {
|
|
2215
|
+
const newAccessToken = await resolveAccessToken({
|
|
2216
|
+
provider,
|
|
2217
|
+
currentTokens: tokenData,
|
|
2218
|
+
providerOAuth: {
|
|
2219
|
+
clientId: credentials.clientId,
|
|
2220
|
+
clientSecret: credentials.clientSecret,
|
|
2221
|
+
subdomain: credentials.config?.subdomain
|
|
2222
|
+
},
|
|
2223
|
+
serverUrl,
|
|
2224
|
+
apiKey: this.mcpApiKey,
|
|
2225
|
+
setProviderToken: this.setTokenCallback,
|
|
2226
|
+
context
|
|
2227
|
+
});
|
|
2228
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
2229
|
+
return tokenData;
|
|
2230
|
+
}
|
|
2231
|
+
if (this.getTokenCallback) {
|
|
2232
|
+
try {
|
|
2233
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
2234
|
+
if (reloaded) {
|
|
2235
|
+
return reloaded;
|
|
2236
|
+
}
|
|
2237
|
+
} catch {}
|
|
2238
|
+
}
|
|
2239
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
2240
|
+
} catch (err) {
|
|
2241
|
+
if (err instanceof RefreshRejectedError) {
|
|
2242
|
+
throw err;
|
|
2243
|
+
}
|
|
2244
|
+
return tokenData;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2046
2247
|
clearProviderToken(provider) {
|
|
2047
2248
|
this.providerTokens.delete(provider);
|
|
2048
2249
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -2501,10 +2702,26 @@ class MCPClientBase {
|
|
|
2501
2702
|
};
|
|
2502
2703
|
this.onReauthRequired = config.onReauthRequired;
|
|
2503
2704
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
2705
|
+
const refreshProviders = {};
|
|
2706
|
+
for (const integration of this.integrations) {
|
|
2707
|
+
const oauth = integration?.oauth;
|
|
2708
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
2709
|
+
refreshProviders[oauth.provider] = {
|
|
2710
|
+
clientId: oauth.clientId,
|
|
2711
|
+
clientSecret: oauth.clientSecret,
|
|
2712
|
+
config: oauth.config
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
const mcpServerUrl = config.serverUrl;
|
|
2504
2717
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
2505
2718
|
getProviderToken: config.getProviderToken,
|
|
2506
2719
|
setProviderToken: config.setProviderToken,
|
|
2507
2720
|
removeProviderToken: config.removeProviderToken
|
|
2721
|
+
}, {
|
|
2722
|
+
providers: refreshProviders,
|
|
2723
|
+
mcpServerUrl,
|
|
2724
|
+
apiKey: config.apiKey
|
|
2508
2725
|
});
|
|
2509
2726
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
2510
2727
|
for (const integration of this.integrations) {
|
|
@@ -9041,12 +9258,15 @@ export {
|
|
|
9041
9258
|
supabaseIntegration,
|
|
9042
9259
|
stripeIntegration,
|
|
9043
9260
|
slackIntegration,
|
|
9261
|
+
shouldRefreshToken,
|
|
9044
9262
|
shopifyIntegration,
|
|
9045
9263
|
sharepointIntegration,
|
|
9046
9264
|
sentryIntegration,
|
|
9047
9265
|
sendCallbackToOpener,
|
|
9048
9266
|
salesforceIntegration,
|
|
9267
|
+
resolveAccessToken,
|
|
9049
9268
|
resendIntegration,
|
|
9269
|
+
refreshViaMcp,
|
|
9050
9270
|
redisIntegration,
|
|
9051
9271
|
redditIntegration,
|
|
9052
9272
|
rampIntegration,
|
|
@@ -9135,6 +9355,8 @@ export {
|
|
|
9135
9355
|
TriggerClient,
|
|
9136
9356
|
ToolCallError,
|
|
9137
9357
|
TokenExpiredError,
|
|
9358
|
+
RefreshTransientError,
|
|
9359
|
+
RefreshRejectedError,
|
|
9138
9360
|
OAuthWindowManager,
|
|
9139
9361
|
OAuthManager,
|
|
9140
9362
|
OAuthHandler,
|
|
@@ -9144,6 +9366,7 @@ export {
|
|
|
9144
9366
|
IntegrateSDKError,
|
|
9145
9367
|
INTEGRATION_CATEGORY_ORDER,
|
|
9146
9368
|
HttpSessionTransport,
|
|
9369
|
+
DEFAULT_REFRESH_WINDOW_MS,
|
|
9147
9370
|
ConnectionError,
|
|
9148
9371
|
AuthorizationError,
|
|
9149
9372
|
AuthenticationError
|