integrate-sdk 0.9.42-dev.0 → 0.9.44-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/auto-routes.js +242 -79
- package/dist/adapters/base-handler.js +242 -79
- package/dist/adapters/index.js +463 -81
- package/dist/adapters/nextjs.js +242 -79
- package/dist/adapters/solid-start.js +463 -81
- package/dist/adapters/svelte-kit.js +463 -81
- package/dist/index.js +459 -73
- package/dist/oauth.js +242 -79
- package/dist/server.js +493 -82
- 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/email-fetcher.d.ts +17 -6
- package/dist/src/oauth/email-fetcher.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/index.js
CHANGED
|
@@ -1671,99 +1671,402 @@ class IndexedDBStorage {
|
|
|
1671
1671
|
|
|
1672
1672
|
// src/oauth/email-fetcher.ts
|
|
1673
1673
|
var logger3 = createLogger("EmailFetcher");
|
|
1674
|
+
var EMAIL_FETCHERS = {
|
|
1675
|
+
github: fetchGitHubEmail,
|
|
1676
|
+
gmail: fetchGoogleEmail,
|
|
1677
|
+
google: fetchGoogleEmail,
|
|
1678
|
+
gcal: fetchGoogleEmail,
|
|
1679
|
+
gdrive: fetchGoogleEmail,
|
|
1680
|
+
gdocs: fetchGoogleEmail,
|
|
1681
|
+
gsheets: fetchGoogleEmail,
|
|
1682
|
+
gslides: fetchGoogleEmail,
|
|
1683
|
+
gcontacts: fetchGoogleEmail,
|
|
1684
|
+
gmeet: fetchGoogleEmail,
|
|
1685
|
+
gchat: fetchGoogleEmail,
|
|
1686
|
+
gtasks: fetchGoogleEmail,
|
|
1687
|
+
ga4: fetchGoogleEmail,
|
|
1688
|
+
youtube: fetchGoogleEmail,
|
|
1689
|
+
notion: fetchNotionEmail,
|
|
1690
|
+
linear: fetchLinearEmail,
|
|
1691
|
+
hubspot: fetchHubSpotEmail,
|
|
1692
|
+
polar: fetchPolarEmail,
|
|
1693
|
+
todoist: fetchTodoistEmail,
|
|
1694
|
+
vercel: fetchVercelEmail,
|
|
1695
|
+
slack: fetchSlackEmail,
|
|
1696
|
+
intercom: fetchIntercomEmail,
|
|
1697
|
+
jira: fetchAtlassianEmail,
|
|
1698
|
+
zendesk: fetchZendeskEmail,
|
|
1699
|
+
airtable: fetchAirtableEmail,
|
|
1700
|
+
discord: fetchDiscordEmail,
|
|
1701
|
+
dropbox: fetchDropboxEmail,
|
|
1702
|
+
gitlab: fetchGitLabEmail,
|
|
1703
|
+
reddit: fetchRedditEmail,
|
|
1704
|
+
outlook: fetchMicrosoftEmail,
|
|
1705
|
+
teams: fetchMicrosoftEmail,
|
|
1706
|
+
onedrive: fetchMicrosoftEmail,
|
|
1707
|
+
sharepoint: fetchMicrosoftEmail,
|
|
1708
|
+
excel: fetchMicrosoftEmail,
|
|
1709
|
+
word: fetchMicrosoftEmail,
|
|
1710
|
+
powerpoint: fetchMicrosoftEmail,
|
|
1711
|
+
planner: fetchMicrosoftEmail
|
|
1712
|
+
};
|
|
1674
1713
|
async function fetchUserEmail(provider, tokenData) {
|
|
1714
|
+
const fetcher = EMAIL_FETCHERS[provider.toLowerCase()];
|
|
1715
|
+
if (!fetcher) {
|
|
1716
|
+
return tokenData.email;
|
|
1717
|
+
}
|
|
1675
1718
|
try {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
return await fetchGitHubEmail(tokenData.accessToken);
|
|
1679
|
-
case "gmail":
|
|
1680
|
-
case "google":
|
|
1681
|
-
return await fetchGoogleEmail(tokenData.accessToken);
|
|
1682
|
-
case "notion":
|
|
1683
|
-
return await fetchNotionEmail(tokenData.accessToken);
|
|
1684
|
-
default:
|
|
1685
|
-
return tokenData.email;
|
|
1686
|
-
}
|
|
1719
|
+
const email = await fetcher(tokenData);
|
|
1720
|
+
return email ?? tokenData.email;
|
|
1687
1721
|
} catch (error) {
|
|
1688
1722
|
logger3.error(`Failed to fetch email for ${provider}:`, error);
|
|
1689
|
-
return;
|
|
1723
|
+
return tokenData.email;
|
|
1690
1724
|
}
|
|
1691
1725
|
}
|
|
1692
|
-
async function fetchGitHubEmail(
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1726
|
+
async function fetchGitHubEmail(token) {
|
|
1727
|
+
const headers = {
|
|
1728
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1729
|
+
Accept: "application/vnd.github.v3+json"
|
|
1730
|
+
};
|
|
1731
|
+
const userResponse = await fetch("https://api.github.com/user", { headers });
|
|
1732
|
+
if (!userResponse.ok)
|
|
1733
|
+
return;
|
|
1734
|
+
const user = await userResponse.json();
|
|
1735
|
+
if (user.email)
|
|
1736
|
+
return user.email;
|
|
1737
|
+
const emailsResponse = await fetch("https://api.github.com/user/emails", { headers });
|
|
1738
|
+
if (!emailsResponse.ok)
|
|
1739
|
+
return;
|
|
1740
|
+
const emails = await emailsResponse.json();
|
|
1741
|
+
const primary = emails.find((e) => e.primary && e.verified);
|
|
1742
|
+
if (primary)
|
|
1743
|
+
return primary.email;
|
|
1744
|
+
const verified = emails.find((e) => e.verified);
|
|
1745
|
+
if (verified)
|
|
1746
|
+
return verified.email;
|
|
1747
|
+
return emails[0]?.email;
|
|
1748
|
+
}
|
|
1749
|
+
async function fetchGoogleEmail(token) {
|
|
1750
|
+
const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
|
|
1751
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1752
|
+
});
|
|
1753
|
+
if (!response.ok)
|
|
1754
|
+
return;
|
|
1755
|
+
const user = await response.json();
|
|
1756
|
+
return user.email;
|
|
1757
|
+
}
|
|
1758
|
+
async function fetchNotionEmail(token) {
|
|
1759
|
+
const response = await fetch("https://api.notion.com/v1/users/me", {
|
|
1760
|
+
headers: {
|
|
1761
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1762
|
+
"Notion-Version": "2022-06-28"
|
|
1715
1763
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1764
|
+
});
|
|
1765
|
+
if (!response.ok)
|
|
1766
|
+
return;
|
|
1767
|
+
const user = await response.json();
|
|
1768
|
+
return user.person?.email ?? user.bot?.owner?.user?.person?.email;
|
|
1769
|
+
}
|
|
1770
|
+
async function fetchLinearEmail(token) {
|
|
1771
|
+
const response = await fetch("https://api.linear.app/graphql", {
|
|
1772
|
+
method: "POST",
|
|
1773
|
+
headers: {
|
|
1774
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1775
|
+
"Content-Type": "application/json"
|
|
1776
|
+
},
|
|
1777
|
+
body: JSON.stringify({ query: "{ viewer { email } }" })
|
|
1778
|
+
});
|
|
1779
|
+
if (!response.ok)
|
|
1780
|
+
return;
|
|
1781
|
+
const body = await response.json();
|
|
1782
|
+
return body.data?.viewer?.email;
|
|
1783
|
+
}
|
|
1784
|
+
async function fetchHubSpotEmail(token) {
|
|
1785
|
+
const url = `https://api.hubapi.com/oauth/v1/access-tokens/${encodeURIComponent(token.accessToken)}`;
|
|
1786
|
+
const response = await fetch(url);
|
|
1787
|
+
if (!response.ok)
|
|
1788
|
+
return;
|
|
1789
|
+
const body = await response.json();
|
|
1790
|
+
return body.user;
|
|
1791
|
+
}
|
|
1792
|
+
async function fetchPolarEmail(token) {
|
|
1793
|
+
const response = await fetch("https://api.polar.sh/v1/oauth2/userinfo", {
|
|
1794
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1795
|
+
});
|
|
1796
|
+
if (!response.ok)
|
|
1797
|
+
return;
|
|
1798
|
+
const body = await response.json();
|
|
1799
|
+
return body.email;
|
|
1800
|
+
}
|
|
1801
|
+
async function fetchTodoistEmail(token) {
|
|
1802
|
+
const response = await fetch("https://api.todoist.com/sync/v9/sync", {
|
|
1803
|
+
method: "POST",
|
|
1804
|
+
headers: {
|
|
1805
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1806
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1807
|
+
},
|
|
1808
|
+
body: 'sync_token=*&resource_types=["user"]'
|
|
1809
|
+
});
|
|
1810
|
+
if (!response.ok)
|
|
1811
|
+
return;
|
|
1812
|
+
const body = await response.json();
|
|
1813
|
+
return body.user?.email;
|
|
1814
|
+
}
|
|
1815
|
+
async function fetchVercelEmail(token) {
|
|
1816
|
+
const response = await fetch("https://api.vercel.com/v2/user", {
|
|
1817
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1818
|
+
});
|
|
1819
|
+
if (!response.ok)
|
|
1820
|
+
return;
|
|
1821
|
+
const body = await response.json();
|
|
1822
|
+
return body.user?.email ?? body.email;
|
|
1823
|
+
}
|
|
1824
|
+
async function fetchSlackEmail(token) {
|
|
1825
|
+
const response = await fetch("https://slack.com/api/users.identity", {
|
|
1826
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1827
|
+
});
|
|
1828
|
+
if (!response.ok)
|
|
1829
|
+
return;
|
|
1830
|
+
const body = await response.json();
|
|
1831
|
+
if (!body.ok)
|
|
1832
|
+
return;
|
|
1833
|
+
return body.user?.email;
|
|
1834
|
+
}
|
|
1835
|
+
async function fetchIntercomEmail(token) {
|
|
1836
|
+
const response = await fetch("https://api.intercom.io/me", {
|
|
1837
|
+
headers: {
|
|
1838
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1839
|
+
Accept: "application/json"
|
|
1720
1840
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1841
|
+
});
|
|
1842
|
+
if (!response.ok)
|
|
1843
|
+
return;
|
|
1844
|
+
const body = await response.json();
|
|
1845
|
+
return body.email;
|
|
1846
|
+
}
|
|
1847
|
+
async function fetchAtlassianEmail(token) {
|
|
1848
|
+
const response = await fetch("https://api.atlassian.com/me", {
|
|
1849
|
+
headers: {
|
|
1850
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1851
|
+
Accept: "application/json"
|
|
1724
1852
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1853
|
+
});
|
|
1854
|
+
if (!response.ok)
|
|
1855
|
+
return;
|
|
1856
|
+
const body = await response.json();
|
|
1857
|
+
return body.email;
|
|
1858
|
+
}
|
|
1859
|
+
async function fetchZendeskEmail(token) {
|
|
1860
|
+
const subdomain = token.providerConfig?.subdomain?.trim();
|
|
1861
|
+
if (!subdomain)
|
|
1862
|
+
return;
|
|
1863
|
+
const response = await fetch(`https://${subdomain}.zendesk.com/api/v2/users/me.json`, {
|
|
1864
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1865
|
+
});
|
|
1866
|
+
if (!response.ok)
|
|
1867
|
+
return;
|
|
1868
|
+
const body = await response.json();
|
|
1869
|
+
return body.user?.email;
|
|
1870
|
+
}
|
|
1871
|
+
async function fetchAirtableEmail(token) {
|
|
1872
|
+
const response = await fetch("https://api.airtable.com/v0/meta/whoami", {
|
|
1873
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1874
|
+
});
|
|
1875
|
+
if (!response.ok)
|
|
1876
|
+
return;
|
|
1877
|
+
const body = await response.json();
|
|
1878
|
+
return body.email;
|
|
1879
|
+
}
|
|
1880
|
+
async function fetchDiscordEmail(token) {
|
|
1881
|
+
const response = await fetch("https://discord.com/api/users/@me", {
|
|
1882
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1883
|
+
});
|
|
1884
|
+
if (!response.ok)
|
|
1885
|
+
return;
|
|
1886
|
+
const body = await response.json();
|
|
1887
|
+
return body.email;
|
|
1888
|
+
}
|
|
1889
|
+
async function fetchDropboxEmail(token) {
|
|
1890
|
+
const response = await fetch("https://api.dropboxapi.com/2/users/get_current_account", {
|
|
1891
|
+
method: "POST",
|
|
1892
|
+
headers: {
|
|
1893
|
+
Authorization: `Bearer ${token.accessToken}`
|
|
1894
|
+
},
|
|
1895
|
+
body: "null"
|
|
1896
|
+
});
|
|
1897
|
+
if (!response.ok)
|
|
1898
|
+
return;
|
|
1899
|
+
const body = await response.json();
|
|
1900
|
+
return body.email;
|
|
1901
|
+
}
|
|
1902
|
+
async function fetchGitLabEmail(token) {
|
|
1903
|
+
const response = await fetch("https://gitlab.com/api/v4/user", {
|
|
1904
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1905
|
+
});
|
|
1906
|
+
if (!response.ok)
|
|
1907
|
+
return;
|
|
1908
|
+
const body = await response.json();
|
|
1909
|
+
return body.email;
|
|
1910
|
+
}
|
|
1911
|
+
async function fetchRedditEmail(token) {
|
|
1912
|
+
const response = await fetch("https://oauth.reddit.com/api/v1/me", {
|
|
1913
|
+
headers: {
|
|
1914
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
1915
|
+
"User-Agent": "integrate-sdk"
|
|
1727
1916
|
}
|
|
1917
|
+
});
|
|
1918
|
+
if (!response.ok)
|
|
1728
1919
|
return;
|
|
1729
|
-
|
|
1730
|
-
|
|
1920
|
+
const body = await response.json();
|
|
1921
|
+
return body.name;
|
|
1922
|
+
}
|
|
1923
|
+
async function fetchMicrosoftEmail(token) {
|
|
1924
|
+
const response = await fetch("https://graph.microsoft.com/v1.0/me", {
|
|
1925
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
1926
|
+
});
|
|
1927
|
+
if (!response.ok)
|
|
1731
1928
|
return;
|
|
1929
|
+
const body = await response.json();
|
|
1930
|
+
return body.mail ?? body.userPrincipalName;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// src/oauth/refresh.ts
|
|
1934
|
+
class RefreshRejectedError extends Error {
|
|
1935
|
+
provider;
|
|
1936
|
+
constructor(provider, message) {
|
|
1937
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
1938
|
+
this.name = "RefreshRejectedError";
|
|
1939
|
+
this.provider = provider;
|
|
1732
1940
|
}
|
|
1733
1941
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1942
|
+
|
|
1943
|
+
class RefreshTransientError extends Error {
|
|
1944
|
+
provider;
|
|
1945
|
+
constructor(provider, message) {
|
|
1946
|
+
super(message);
|
|
1947
|
+
this.name = "RefreshTransientError";
|
|
1948
|
+
this.provider = provider;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
1952
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
1953
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
1954
|
+
return false;
|
|
1955
|
+
}
|
|
1956
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
1957
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
1961
|
+
}
|
|
1962
|
+
async function refreshViaMcp(opts) {
|
|
1963
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
1964
|
+
const body = {
|
|
1965
|
+
provider: opts.provider,
|
|
1966
|
+
refresh_token: opts.refreshToken,
|
|
1967
|
+
client_id: opts.clientId
|
|
1968
|
+
};
|
|
1969
|
+
if (opts.clientSecret) {
|
|
1970
|
+
body.client_secret = opts.clientSecret;
|
|
1971
|
+
}
|
|
1972
|
+
if (opts.subdomain) {
|
|
1973
|
+
body.subdomain = opts.subdomain;
|
|
1974
|
+
}
|
|
1975
|
+
if (opts.extraConfig) {
|
|
1976
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
1977
|
+
if (body[key] === undefined) {
|
|
1978
|
+
body[key] = value;
|
|
1739
1979
|
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
const headers = { "Content-Type": "application/json" };
|
|
1983
|
+
if (opts.apiKey) {
|
|
1984
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
1985
|
+
}
|
|
1986
|
+
let response;
|
|
1987
|
+
try {
|
|
1988
|
+
response = await fetch(url.toString(), {
|
|
1989
|
+
method: "POST",
|
|
1990
|
+
headers,
|
|
1991
|
+
body: JSON.stringify(body),
|
|
1992
|
+
signal: opts.signal
|
|
1740
1993
|
});
|
|
1741
|
-
|
|
1742
|
-
|
|
1994
|
+
} catch (err) {
|
|
1995
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
1996
|
+
}
|
|
1997
|
+
if (response.status === 401) {
|
|
1998
|
+
let parsed = {};
|
|
1999
|
+
try {
|
|
2000
|
+
parsed = await response.json();
|
|
2001
|
+
} catch {}
|
|
2002
|
+
if (parsed.error === "invalid_grant") {
|
|
2003
|
+
throw new RefreshRejectedError(opts.provider);
|
|
1743
2004
|
}
|
|
1744
|
-
|
|
1745
|
-
return user.email;
|
|
1746
|
-
} catch (error) {
|
|
1747
|
-
logger3.error("Failed to fetch Google email:", error);
|
|
1748
|
-
return;
|
|
2005
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
1749
2006
|
}
|
|
2007
|
+
if (!response.ok) {
|
|
2008
|
+
let text = "";
|
|
2009
|
+
try {
|
|
2010
|
+
text = await response.text();
|
|
2011
|
+
} catch {}
|
|
2012
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
2013
|
+
}
|
|
2014
|
+
let result;
|
|
2015
|
+
try {
|
|
2016
|
+
result = await response.json();
|
|
2017
|
+
} catch (err) {
|
|
2018
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
2019
|
+
}
|
|
2020
|
+
if (!result.accessToken) {
|
|
2021
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
2022
|
+
}
|
|
2023
|
+
if (!result.refreshToken) {
|
|
2024
|
+
result.refreshToken = opts.refreshToken;
|
|
2025
|
+
}
|
|
2026
|
+
return result;
|
|
1750
2027
|
}
|
|
1751
|
-
async function
|
|
2028
|
+
async function resolveAccessToken(opts) {
|
|
2029
|
+
const { provider, currentTokens, force = false } = opts;
|
|
2030
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
2031
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
2032
|
+
return currentTokens.accessToken;
|
|
2033
|
+
}
|
|
1752
2034
|
try {
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
2035
|
+
const refreshed = await refreshViaMcp({
|
|
2036
|
+
provider,
|
|
2037
|
+
refreshToken: currentTokens.refreshToken,
|
|
2038
|
+
clientId: opts.providerOAuth.clientId,
|
|
2039
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
2040
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
2041
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
2042
|
+
serverUrl: opts.serverUrl,
|
|
2043
|
+
apiKey: opts.apiKey
|
|
1758
2044
|
});
|
|
1759
|
-
|
|
1760
|
-
|
|
2045
|
+
const updated = {
|
|
2046
|
+
...currentTokens,
|
|
2047
|
+
accessToken: refreshed.accessToken,
|
|
2048
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
2049
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
2050
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
2051
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
2052
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
2053
|
+
};
|
|
2054
|
+
if (opts.setProviderToken) {
|
|
2055
|
+
try {
|
|
2056
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
2057
|
+
} catch {}
|
|
1761
2058
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
2059
|
+
return updated.accessToken;
|
|
2060
|
+
} catch (err) {
|
|
2061
|
+
if (err instanceof RefreshRejectedError) {
|
|
2062
|
+
if (opts.setProviderToken) {
|
|
2063
|
+
try {
|
|
2064
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
2065
|
+
} catch {}
|
|
2066
|
+
}
|
|
2067
|
+
throw err;
|
|
2068
|
+
}
|
|
2069
|
+
return currentTokens.accessToken;
|
|
1767
2070
|
}
|
|
1768
2071
|
}
|
|
1769
2072
|
|
|
@@ -1782,7 +2085,10 @@ class OAuthManager {
|
|
|
1782
2085
|
removeTokenCallback;
|
|
1783
2086
|
indexedDBStorage;
|
|
1784
2087
|
skipLocalStorage = false;
|
|
1785
|
-
|
|
2088
|
+
providerOAuth = {};
|
|
2089
|
+
mcpServerUrl;
|
|
2090
|
+
mcpApiKey;
|
|
2091
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
1786
2092
|
this.oauthApiBase = oauthApiBase;
|
|
1787
2093
|
this.apiBaseUrl = apiBaseUrl;
|
|
1788
2094
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -1794,6 +2100,9 @@ class OAuthManager {
|
|
|
1794
2100
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
1795
2101
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
1796
2102
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
2103
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
2104
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
2105
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
1797
2106
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
1798
2107
|
this.cleanupExpiredPendingAuths();
|
|
1799
2108
|
}
|
|
@@ -2003,7 +2312,9 @@ class OAuthManager {
|
|
|
2003
2312
|
try {
|
|
2004
2313
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
2005
2314
|
if (tokenData) {
|
|
2006
|
-
this.
|
|
2315
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
2316
|
+
this.providerTokens.set(provider, refreshed);
|
|
2317
|
+
return refreshed;
|
|
2007
2318
|
}
|
|
2008
2319
|
return tokenData;
|
|
2009
2320
|
} catch (error) {
|
|
@@ -2043,6 +2354,59 @@ class OAuthManager {
|
|
|
2043
2354
|
}
|
|
2044
2355
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
2045
2356
|
}
|
|
2357
|
+
configureTokenRefresh(refreshConfig) {
|
|
2358
|
+
if (refreshConfig.providers) {
|
|
2359
|
+
this.providerOAuth = refreshConfig.providers;
|
|
2360
|
+
}
|
|
2361
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
2362
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
2363
|
+
}
|
|
2364
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
2365
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
2369
|
+
const credentials = this.providerOAuth[provider];
|
|
2370
|
+
const serverUrl = this.mcpServerUrl;
|
|
2371
|
+
if (!credentials || !serverUrl) {
|
|
2372
|
+
return tokenData;
|
|
2373
|
+
}
|
|
2374
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
2375
|
+
return tokenData;
|
|
2376
|
+
}
|
|
2377
|
+
try {
|
|
2378
|
+
const newAccessToken = await resolveAccessToken({
|
|
2379
|
+
provider,
|
|
2380
|
+
currentTokens: tokenData,
|
|
2381
|
+
providerOAuth: {
|
|
2382
|
+
clientId: credentials.clientId,
|
|
2383
|
+
clientSecret: credentials.clientSecret,
|
|
2384
|
+
subdomain: credentials.config?.subdomain
|
|
2385
|
+
},
|
|
2386
|
+
serverUrl,
|
|
2387
|
+
apiKey: this.mcpApiKey,
|
|
2388
|
+
setProviderToken: this.setTokenCallback,
|
|
2389
|
+
context
|
|
2390
|
+
});
|
|
2391
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
2392
|
+
return tokenData;
|
|
2393
|
+
}
|
|
2394
|
+
if (this.getTokenCallback) {
|
|
2395
|
+
try {
|
|
2396
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
2397
|
+
if (reloaded) {
|
|
2398
|
+
return reloaded;
|
|
2399
|
+
}
|
|
2400
|
+
} catch {}
|
|
2401
|
+
}
|
|
2402
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
2403
|
+
} catch (err) {
|
|
2404
|
+
if (err instanceof RefreshRejectedError) {
|
|
2405
|
+
throw err;
|
|
2406
|
+
}
|
|
2407
|
+
return tokenData;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2046
2410
|
clearProviderToken(provider) {
|
|
2047
2411
|
this.providerTokens.delete(provider);
|
|
2048
2412
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -2501,10 +2865,26 @@ class MCPClientBase {
|
|
|
2501
2865
|
};
|
|
2502
2866
|
this.onReauthRequired = config.onReauthRequired;
|
|
2503
2867
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
2868
|
+
const refreshProviders = {};
|
|
2869
|
+
for (const integration of this.integrations) {
|
|
2870
|
+
const oauth = integration?.oauth;
|
|
2871
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
2872
|
+
refreshProviders[oauth.provider] = {
|
|
2873
|
+
clientId: oauth.clientId,
|
|
2874
|
+
clientSecret: oauth.clientSecret,
|
|
2875
|
+
config: oauth.config
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
const mcpServerUrl = config.serverUrl;
|
|
2504
2880
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
2505
2881
|
getProviderToken: config.getProviderToken,
|
|
2506
2882
|
setProviderToken: config.setProviderToken,
|
|
2507
2883
|
removeProviderToken: config.removeProviderToken
|
|
2884
|
+
}, {
|
|
2885
|
+
providers: refreshProviders,
|
|
2886
|
+
mcpServerUrl,
|
|
2887
|
+
apiKey: config.apiKey
|
|
2508
2888
|
});
|
|
2509
2889
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
2510
2890
|
for (const integration of this.integrations) {
|
|
@@ -9041,12 +9421,15 @@ export {
|
|
|
9041
9421
|
supabaseIntegration,
|
|
9042
9422
|
stripeIntegration,
|
|
9043
9423
|
slackIntegration,
|
|
9424
|
+
shouldRefreshToken,
|
|
9044
9425
|
shopifyIntegration,
|
|
9045
9426
|
sharepointIntegration,
|
|
9046
9427
|
sentryIntegration,
|
|
9047
9428
|
sendCallbackToOpener,
|
|
9048
9429
|
salesforceIntegration,
|
|
9430
|
+
resolveAccessToken,
|
|
9049
9431
|
resendIntegration,
|
|
9432
|
+
refreshViaMcp,
|
|
9050
9433
|
redisIntegration,
|
|
9051
9434
|
redditIntegration,
|
|
9052
9435
|
rampIntegration,
|
|
@@ -9135,6 +9518,8 @@ export {
|
|
|
9135
9518
|
TriggerClient,
|
|
9136
9519
|
ToolCallError,
|
|
9137
9520
|
TokenExpiredError,
|
|
9521
|
+
RefreshTransientError,
|
|
9522
|
+
RefreshRejectedError,
|
|
9138
9523
|
OAuthWindowManager,
|
|
9139
9524
|
OAuthManager,
|
|
9140
9525
|
OAuthHandler,
|
|
@@ -9144,6 +9529,7 @@ export {
|
|
|
9144
9529
|
IntegrateSDKError,
|
|
9145
9530
|
INTEGRATION_CATEGORY_ORDER,
|
|
9146
9531
|
HttpSessionTransport,
|
|
9532
|
+
DEFAULT_REFRESH_WINDOW_MS,
|
|
9147
9533
|
ConnectionError,
|
|
9148
9534
|
AuthorizationError,
|
|
9149
9535
|
AuthenticationError
|