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/adapters/index.js
CHANGED
|
@@ -740,104 +740,267 @@ var init_logger = __esm(() => {
|
|
|
740
740
|
|
|
741
741
|
// ../oauth/email-fetcher.ts
|
|
742
742
|
async function fetchUserEmail(provider, tokenData) {
|
|
743
|
+
const fetcher = EMAIL_FETCHERS[provider.toLowerCase()];
|
|
744
|
+
if (!fetcher) {
|
|
745
|
+
return tokenData.email;
|
|
746
|
+
}
|
|
743
747
|
try {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return await fetchGitHubEmail(tokenData.accessToken);
|
|
747
|
-
case "gmail":
|
|
748
|
-
case "google":
|
|
749
|
-
return await fetchGoogleEmail(tokenData.accessToken);
|
|
750
|
-
case "notion":
|
|
751
|
-
return await fetchNotionEmail(tokenData.accessToken);
|
|
752
|
-
default:
|
|
753
|
-
return tokenData.email;
|
|
754
|
-
}
|
|
748
|
+
const email = await fetcher(tokenData);
|
|
749
|
+
return email ?? tokenData.email;
|
|
755
750
|
} catch (error) {
|
|
756
751
|
logger.error(`Failed to fetch email for ${provider}:`, error);
|
|
757
|
-
return;
|
|
752
|
+
return tokenData.email;
|
|
758
753
|
}
|
|
759
754
|
}
|
|
760
|
-
async function fetchGitHubEmail(
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
755
|
+
async function fetchGitHubEmail(token) {
|
|
756
|
+
const headers = {
|
|
757
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
758
|
+
Accept: "application/vnd.github.v3+json"
|
|
759
|
+
};
|
|
760
|
+
const userResponse = await fetch("https://api.github.com/user", { headers });
|
|
761
|
+
if (!userResponse.ok)
|
|
762
|
+
return;
|
|
763
|
+
const user = await userResponse.json();
|
|
764
|
+
if (user.email)
|
|
765
|
+
return user.email;
|
|
766
|
+
const emailsResponse = await fetch("https://api.github.com/user/emails", { headers });
|
|
767
|
+
if (!emailsResponse.ok)
|
|
768
|
+
return;
|
|
769
|
+
const emails = await emailsResponse.json();
|
|
770
|
+
const primary = emails.find((e) => e.primary && e.verified);
|
|
771
|
+
if (primary)
|
|
772
|
+
return primary.email;
|
|
773
|
+
const verified = emails.find((e) => e.verified);
|
|
774
|
+
if (verified)
|
|
775
|
+
return verified.email;
|
|
776
|
+
return emails[0]?.email;
|
|
777
|
+
}
|
|
778
|
+
async function fetchGoogleEmail(token) {
|
|
779
|
+
const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
|
|
780
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
781
|
+
});
|
|
782
|
+
if (!response.ok)
|
|
783
|
+
return;
|
|
784
|
+
const user = await response.json();
|
|
785
|
+
return user.email;
|
|
786
|
+
}
|
|
787
|
+
async function fetchNotionEmail(token) {
|
|
788
|
+
const response = await fetch("https://api.notion.com/v1/users/me", {
|
|
789
|
+
headers: {
|
|
790
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
791
|
+
"Notion-Version": "2022-06-28"
|
|
788
792
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
793
|
+
});
|
|
794
|
+
if (!response.ok)
|
|
795
|
+
return;
|
|
796
|
+
const user = await response.json();
|
|
797
|
+
return user.person?.email ?? user.bot?.owner?.user?.person?.email;
|
|
798
|
+
}
|
|
799
|
+
async function fetchLinearEmail(token) {
|
|
800
|
+
const response = await fetch("https://api.linear.app/graphql", {
|
|
801
|
+
method: "POST",
|
|
802
|
+
headers: {
|
|
803
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
804
|
+
"Content-Type": "application/json"
|
|
805
|
+
},
|
|
806
|
+
body: JSON.stringify({ query: "{ viewer { email } }" })
|
|
807
|
+
});
|
|
808
|
+
if (!response.ok)
|
|
809
|
+
return;
|
|
810
|
+
const body = await response.json();
|
|
811
|
+
return body.data?.viewer?.email;
|
|
812
|
+
}
|
|
813
|
+
async function fetchHubSpotEmail(token) {
|
|
814
|
+
const url = `https://api.hubapi.com/oauth/v1/access-tokens/${encodeURIComponent(token.accessToken)}`;
|
|
815
|
+
const response = await fetch(url);
|
|
816
|
+
if (!response.ok)
|
|
817
|
+
return;
|
|
818
|
+
const body = await response.json();
|
|
819
|
+
return body.user;
|
|
820
|
+
}
|
|
821
|
+
async function fetchPolarEmail(token) {
|
|
822
|
+
const response = await fetch("https://api.polar.sh/v1/oauth2/userinfo", {
|
|
823
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
824
|
+
});
|
|
825
|
+
if (!response.ok)
|
|
826
|
+
return;
|
|
827
|
+
const body = await response.json();
|
|
828
|
+
return body.email;
|
|
829
|
+
}
|
|
830
|
+
async function fetchTodoistEmail(token) {
|
|
831
|
+
const response = await fetch("https://api.todoist.com/sync/v9/sync", {
|
|
832
|
+
method: "POST",
|
|
833
|
+
headers: {
|
|
834
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
835
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
836
|
+
},
|
|
837
|
+
body: 'sync_token=*&resource_types=["user"]'
|
|
838
|
+
});
|
|
839
|
+
if (!response.ok)
|
|
840
|
+
return;
|
|
841
|
+
const body = await response.json();
|
|
842
|
+
return body.user?.email;
|
|
843
|
+
}
|
|
844
|
+
async function fetchVercelEmail(token) {
|
|
845
|
+
const response = await fetch("https://api.vercel.com/v2/user", {
|
|
846
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
847
|
+
});
|
|
848
|
+
if (!response.ok)
|
|
849
|
+
return;
|
|
850
|
+
const body = await response.json();
|
|
851
|
+
return body.user?.email ?? body.email;
|
|
852
|
+
}
|
|
853
|
+
async function fetchSlackEmail(token) {
|
|
854
|
+
const response = await fetch("https://slack.com/api/users.identity", {
|
|
855
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
856
|
+
});
|
|
857
|
+
if (!response.ok)
|
|
858
|
+
return;
|
|
859
|
+
const body = await response.json();
|
|
860
|
+
if (!body.ok)
|
|
861
|
+
return;
|
|
862
|
+
return body.user?.email;
|
|
863
|
+
}
|
|
864
|
+
async function fetchIntercomEmail(token) {
|
|
865
|
+
const response = await fetch("https://api.intercom.io/me", {
|
|
866
|
+
headers: {
|
|
867
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
868
|
+
Accept: "application/json"
|
|
792
869
|
}
|
|
793
|
-
|
|
794
|
-
|
|
870
|
+
});
|
|
871
|
+
if (!response.ok)
|
|
872
|
+
return;
|
|
873
|
+
const body = await response.json();
|
|
874
|
+
return body.email;
|
|
875
|
+
}
|
|
876
|
+
async function fetchAtlassianEmail(token) {
|
|
877
|
+
const response = await fetch("https://api.atlassian.com/me", {
|
|
878
|
+
headers: {
|
|
879
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
880
|
+
Accept: "application/json"
|
|
795
881
|
}
|
|
882
|
+
});
|
|
883
|
+
if (!response.ok)
|
|
796
884
|
return;
|
|
797
|
-
|
|
798
|
-
|
|
885
|
+
const body = await response.json();
|
|
886
|
+
return body.email;
|
|
887
|
+
}
|
|
888
|
+
async function fetchZendeskEmail(token) {
|
|
889
|
+
const subdomain = token.providerConfig?.subdomain?.trim();
|
|
890
|
+
if (!subdomain)
|
|
799
891
|
return;
|
|
800
|
-
}
|
|
892
|
+
const response = await fetch(`https://${subdomain}.zendesk.com/api/v2/users/me.json`, {
|
|
893
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
894
|
+
});
|
|
895
|
+
if (!response.ok)
|
|
896
|
+
return;
|
|
897
|
+
const body = await response.json();
|
|
898
|
+
return body.user?.email;
|
|
801
899
|
}
|
|
802
|
-
async function
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
}
|
|
808
|
-
});
|
|
809
|
-
if (!response.ok) {
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
const user = await response.json();
|
|
813
|
-
return user.email;
|
|
814
|
-
} catch (error) {
|
|
815
|
-
logger.error("Failed to fetch Google email:", error);
|
|
900
|
+
async function fetchAirtableEmail(token) {
|
|
901
|
+
const response = await fetch("https://api.airtable.com/v0/meta/whoami", {
|
|
902
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
903
|
+
});
|
|
904
|
+
if (!response.ok)
|
|
816
905
|
return;
|
|
817
|
-
|
|
906
|
+
const body = await response.json();
|
|
907
|
+
return body.email;
|
|
818
908
|
}
|
|
819
|
-
async function
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
909
|
+
async function fetchDiscordEmail(token) {
|
|
910
|
+
const response = await fetch("https://discord.com/api/users/@me", {
|
|
911
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
912
|
+
});
|
|
913
|
+
if (!response.ok)
|
|
914
|
+
return;
|
|
915
|
+
const body = await response.json();
|
|
916
|
+
return body.email;
|
|
917
|
+
}
|
|
918
|
+
async function fetchDropboxEmail(token) {
|
|
919
|
+
const response = await fetch("https://api.dropboxapi.com/2/users/get_current_account", {
|
|
920
|
+
method: "POST",
|
|
921
|
+
headers: {
|
|
922
|
+
Authorization: `Bearer ${token.accessToken}`
|
|
923
|
+
},
|
|
924
|
+
body: "null"
|
|
925
|
+
});
|
|
926
|
+
if (!response.ok)
|
|
927
|
+
return;
|
|
928
|
+
const body = await response.json();
|
|
929
|
+
return body.email;
|
|
930
|
+
}
|
|
931
|
+
async function fetchGitLabEmail(token) {
|
|
932
|
+
const response = await fetch("https://gitlab.com/api/v4/user", {
|
|
933
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
934
|
+
});
|
|
935
|
+
if (!response.ok)
|
|
936
|
+
return;
|
|
937
|
+
const body = await response.json();
|
|
938
|
+
return body.email;
|
|
939
|
+
}
|
|
940
|
+
async function fetchRedditEmail(token) {
|
|
941
|
+
const response = await fetch("https://oauth.reddit.com/api/v1/me", {
|
|
942
|
+
headers: {
|
|
943
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
944
|
+
"User-Agent": "integrate-sdk"
|
|
829
945
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
} catch (error) {
|
|
833
|
-
logger.error("Failed to fetch Notion email:", error);
|
|
946
|
+
});
|
|
947
|
+
if (!response.ok)
|
|
834
948
|
return;
|
|
835
|
-
|
|
949
|
+
const body = await response.json();
|
|
950
|
+
return body.name;
|
|
836
951
|
}
|
|
837
|
-
|
|
952
|
+
async function fetchMicrosoftEmail(token) {
|
|
953
|
+
const response = await fetch("https://graph.microsoft.com/v1.0/me", {
|
|
954
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
955
|
+
});
|
|
956
|
+
if (!response.ok)
|
|
957
|
+
return;
|
|
958
|
+
const body = await response.json();
|
|
959
|
+
return body.mail ?? body.userPrincipalName;
|
|
960
|
+
}
|
|
961
|
+
var logger, EMAIL_FETCHERS;
|
|
838
962
|
var init_email_fetcher = __esm(() => {
|
|
839
963
|
init_logger();
|
|
840
964
|
logger = createLogger("EmailFetcher");
|
|
965
|
+
EMAIL_FETCHERS = {
|
|
966
|
+
github: fetchGitHubEmail,
|
|
967
|
+
gmail: fetchGoogleEmail,
|
|
968
|
+
google: fetchGoogleEmail,
|
|
969
|
+
gcal: fetchGoogleEmail,
|
|
970
|
+
gdrive: fetchGoogleEmail,
|
|
971
|
+
gdocs: fetchGoogleEmail,
|
|
972
|
+
gsheets: fetchGoogleEmail,
|
|
973
|
+
gslides: fetchGoogleEmail,
|
|
974
|
+
gcontacts: fetchGoogleEmail,
|
|
975
|
+
gmeet: fetchGoogleEmail,
|
|
976
|
+
gchat: fetchGoogleEmail,
|
|
977
|
+
gtasks: fetchGoogleEmail,
|
|
978
|
+
ga4: fetchGoogleEmail,
|
|
979
|
+
youtube: fetchGoogleEmail,
|
|
980
|
+
notion: fetchNotionEmail,
|
|
981
|
+
linear: fetchLinearEmail,
|
|
982
|
+
hubspot: fetchHubSpotEmail,
|
|
983
|
+
polar: fetchPolarEmail,
|
|
984
|
+
todoist: fetchTodoistEmail,
|
|
985
|
+
vercel: fetchVercelEmail,
|
|
986
|
+
slack: fetchSlackEmail,
|
|
987
|
+
intercom: fetchIntercomEmail,
|
|
988
|
+
jira: fetchAtlassianEmail,
|
|
989
|
+
zendesk: fetchZendeskEmail,
|
|
990
|
+
airtable: fetchAirtableEmail,
|
|
991
|
+
discord: fetchDiscordEmail,
|
|
992
|
+
dropbox: fetchDropboxEmail,
|
|
993
|
+
gitlab: fetchGitLabEmail,
|
|
994
|
+
reddit: fetchRedditEmail,
|
|
995
|
+
outlook: fetchMicrosoftEmail,
|
|
996
|
+
teams: fetchMicrosoftEmail,
|
|
997
|
+
onedrive: fetchMicrosoftEmail,
|
|
998
|
+
sharepoint: fetchMicrosoftEmail,
|
|
999
|
+
excel: fetchMicrosoftEmail,
|
|
1000
|
+
word: fetchMicrosoftEmail,
|
|
1001
|
+
powerpoint: fetchMicrosoftEmail,
|
|
1002
|
+
planner: fetchMicrosoftEmail
|
|
1003
|
+
};
|
|
841
1004
|
});
|
|
842
1005
|
|
|
843
1006
|
// base-handler.ts
|
|
@@ -2556,6 +2719,148 @@ class IndexedDBStorage {
|
|
|
2556
2719
|
|
|
2557
2720
|
// ../oauth/manager.ts
|
|
2558
2721
|
init_email_fetcher();
|
|
2722
|
+
|
|
2723
|
+
// ../oauth/refresh.ts
|
|
2724
|
+
class RefreshRejectedError extends Error {
|
|
2725
|
+
provider;
|
|
2726
|
+
constructor(provider, message) {
|
|
2727
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
2728
|
+
this.name = "RefreshRejectedError";
|
|
2729
|
+
this.provider = provider;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
class RefreshTransientError extends Error {
|
|
2734
|
+
provider;
|
|
2735
|
+
constructor(provider, message) {
|
|
2736
|
+
super(message);
|
|
2737
|
+
this.name = "RefreshTransientError";
|
|
2738
|
+
this.provider = provider;
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
2742
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
2743
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
2744
|
+
return false;
|
|
2745
|
+
}
|
|
2746
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
2747
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
2748
|
+
return false;
|
|
2749
|
+
}
|
|
2750
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
2751
|
+
}
|
|
2752
|
+
async function refreshViaMcp(opts) {
|
|
2753
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
2754
|
+
const body = {
|
|
2755
|
+
provider: opts.provider,
|
|
2756
|
+
refresh_token: opts.refreshToken,
|
|
2757
|
+
client_id: opts.clientId
|
|
2758
|
+
};
|
|
2759
|
+
if (opts.clientSecret) {
|
|
2760
|
+
body.client_secret = opts.clientSecret;
|
|
2761
|
+
}
|
|
2762
|
+
if (opts.subdomain) {
|
|
2763
|
+
body.subdomain = opts.subdomain;
|
|
2764
|
+
}
|
|
2765
|
+
if (opts.extraConfig) {
|
|
2766
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
2767
|
+
if (body[key] === undefined) {
|
|
2768
|
+
body[key] = value;
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
const headers = { "Content-Type": "application/json" };
|
|
2773
|
+
if (opts.apiKey) {
|
|
2774
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
2775
|
+
}
|
|
2776
|
+
let response;
|
|
2777
|
+
try {
|
|
2778
|
+
response = await fetch(url.toString(), {
|
|
2779
|
+
method: "POST",
|
|
2780
|
+
headers,
|
|
2781
|
+
body: JSON.stringify(body),
|
|
2782
|
+
signal: opts.signal
|
|
2783
|
+
});
|
|
2784
|
+
} catch (err) {
|
|
2785
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
2786
|
+
}
|
|
2787
|
+
if (response.status === 401) {
|
|
2788
|
+
let parsed = {};
|
|
2789
|
+
try {
|
|
2790
|
+
parsed = await response.json();
|
|
2791
|
+
} catch {}
|
|
2792
|
+
if (parsed.error === "invalid_grant") {
|
|
2793
|
+
throw new RefreshRejectedError(opts.provider);
|
|
2794
|
+
}
|
|
2795
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
2796
|
+
}
|
|
2797
|
+
if (!response.ok) {
|
|
2798
|
+
let text = "";
|
|
2799
|
+
try {
|
|
2800
|
+
text = await response.text();
|
|
2801
|
+
} catch {}
|
|
2802
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
2803
|
+
}
|
|
2804
|
+
let result;
|
|
2805
|
+
try {
|
|
2806
|
+
result = await response.json();
|
|
2807
|
+
} catch (err) {
|
|
2808
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
2809
|
+
}
|
|
2810
|
+
if (!result.accessToken) {
|
|
2811
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
2812
|
+
}
|
|
2813
|
+
if (!result.refreshToken) {
|
|
2814
|
+
result.refreshToken = opts.refreshToken;
|
|
2815
|
+
}
|
|
2816
|
+
return result;
|
|
2817
|
+
}
|
|
2818
|
+
async function resolveAccessToken(opts) {
|
|
2819
|
+
const { provider, currentTokens, force = false } = opts;
|
|
2820
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
2821
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
2822
|
+
return currentTokens.accessToken;
|
|
2823
|
+
}
|
|
2824
|
+
try {
|
|
2825
|
+
const refreshed = await refreshViaMcp({
|
|
2826
|
+
provider,
|
|
2827
|
+
refreshToken: currentTokens.refreshToken,
|
|
2828
|
+
clientId: opts.providerOAuth.clientId,
|
|
2829
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
2830
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
2831
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
2832
|
+
serverUrl: opts.serverUrl,
|
|
2833
|
+
apiKey: opts.apiKey
|
|
2834
|
+
});
|
|
2835
|
+
const updated = {
|
|
2836
|
+
...currentTokens,
|
|
2837
|
+
accessToken: refreshed.accessToken,
|
|
2838
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
2839
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
2840
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
2841
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
2842
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
2843
|
+
};
|
|
2844
|
+
if (opts.setProviderToken) {
|
|
2845
|
+
try {
|
|
2846
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
2847
|
+
} catch {}
|
|
2848
|
+
}
|
|
2849
|
+
return updated.accessToken;
|
|
2850
|
+
} catch (err) {
|
|
2851
|
+
if (err instanceof RefreshRejectedError) {
|
|
2852
|
+
if (opts.setProviderToken) {
|
|
2853
|
+
try {
|
|
2854
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
2855
|
+
} catch {}
|
|
2856
|
+
}
|
|
2857
|
+
throw err;
|
|
2858
|
+
}
|
|
2859
|
+
return currentTokens.accessToken;
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// ../oauth/manager.ts
|
|
2559
2864
|
init_logger();
|
|
2560
2865
|
var logger6 = createLogger("OAuth");
|
|
2561
2866
|
|
|
@@ -2571,7 +2876,10 @@ class OAuthManager {
|
|
|
2571
2876
|
removeTokenCallback;
|
|
2572
2877
|
indexedDBStorage;
|
|
2573
2878
|
skipLocalStorage = false;
|
|
2574
|
-
|
|
2879
|
+
providerOAuth = {};
|
|
2880
|
+
mcpServerUrl;
|
|
2881
|
+
mcpApiKey;
|
|
2882
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
2575
2883
|
this.oauthApiBase = oauthApiBase;
|
|
2576
2884
|
this.apiBaseUrl = apiBaseUrl;
|
|
2577
2885
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -2583,6 +2891,9 @@ class OAuthManager {
|
|
|
2583
2891
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
2584
2892
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
2585
2893
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
2894
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
2895
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
2896
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
2586
2897
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
2587
2898
|
this.cleanupExpiredPendingAuths();
|
|
2588
2899
|
}
|
|
@@ -2792,7 +3103,9 @@ class OAuthManager {
|
|
|
2792
3103
|
try {
|
|
2793
3104
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
2794
3105
|
if (tokenData) {
|
|
2795
|
-
this.
|
|
3106
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
3107
|
+
this.providerTokens.set(provider, refreshed);
|
|
3108
|
+
return refreshed;
|
|
2796
3109
|
}
|
|
2797
3110
|
return tokenData;
|
|
2798
3111
|
} catch (error) {
|
|
@@ -2832,6 +3145,59 @@ class OAuthManager {
|
|
|
2832
3145
|
}
|
|
2833
3146
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
2834
3147
|
}
|
|
3148
|
+
configureTokenRefresh(refreshConfig) {
|
|
3149
|
+
if (refreshConfig.providers) {
|
|
3150
|
+
this.providerOAuth = refreshConfig.providers;
|
|
3151
|
+
}
|
|
3152
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
3153
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
3154
|
+
}
|
|
3155
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
3156
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
3160
|
+
const credentials = this.providerOAuth[provider];
|
|
3161
|
+
const serverUrl = this.mcpServerUrl;
|
|
3162
|
+
if (!credentials || !serverUrl) {
|
|
3163
|
+
return tokenData;
|
|
3164
|
+
}
|
|
3165
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
3166
|
+
return tokenData;
|
|
3167
|
+
}
|
|
3168
|
+
try {
|
|
3169
|
+
const newAccessToken = await resolveAccessToken({
|
|
3170
|
+
provider,
|
|
3171
|
+
currentTokens: tokenData,
|
|
3172
|
+
providerOAuth: {
|
|
3173
|
+
clientId: credentials.clientId,
|
|
3174
|
+
clientSecret: credentials.clientSecret,
|
|
3175
|
+
subdomain: credentials.config?.subdomain
|
|
3176
|
+
},
|
|
3177
|
+
serverUrl,
|
|
3178
|
+
apiKey: this.mcpApiKey,
|
|
3179
|
+
setProviderToken: this.setTokenCallback,
|
|
3180
|
+
context
|
|
3181
|
+
});
|
|
3182
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
3183
|
+
return tokenData;
|
|
3184
|
+
}
|
|
3185
|
+
if (this.getTokenCallback) {
|
|
3186
|
+
try {
|
|
3187
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
3188
|
+
if (reloaded) {
|
|
3189
|
+
return reloaded;
|
|
3190
|
+
}
|
|
3191
|
+
} catch {}
|
|
3192
|
+
}
|
|
3193
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
3194
|
+
} catch (err) {
|
|
3195
|
+
if (err instanceof RefreshRejectedError) {
|
|
3196
|
+
throw err;
|
|
3197
|
+
}
|
|
3198
|
+
return tokenData;
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
2835
3201
|
clearProviderToken(provider) {
|
|
2836
3202
|
this.providerTokens.delete(provider);
|
|
2837
3203
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -3288,10 +3654,26 @@ class MCPClientBase {
|
|
|
3288
3654
|
};
|
|
3289
3655
|
this.onReauthRequired = config.onReauthRequired;
|
|
3290
3656
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
3657
|
+
const refreshProviders = {};
|
|
3658
|
+
for (const integration of this.integrations) {
|
|
3659
|
+
const oauth = integration?.oauth;
|
|
3660
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
3661
|
+
refreshProviders[oauth.provider] = {
|
|
3662
|
+
clientId: oauth.clientId,
|
|
3663
|
+
clientSecret: oauth.clientSecret,
|
|
3664
|
+
config: oauth.config
|
|
3665
|
+
};
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
const mcpServerUrl = config.serverUrl;
|
|
3291
3669
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
3292
3670
|
getProviderToken: config.getProviderToken,
|
|
3293
3671
|
setProviderToken: config.setProviderToken,
|
|
3294
3672
|
removeProviderToken: config.removeProviderToken
|
|
3673
|
+
}, {
|
|
3674
|
+
providers: refreshProviders,
|
|
3675
|
+
mcpServerUrl,
|
|
3676
|
+
apiKey: config.apiKey
|
|
3295
3677
|
});
|
|
3296
3678
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
3297
3679
|
for (const integration of this.integrations) {
|