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/server.js
CHANGED
|
@@ -736,104 +736,267 @@ function base64UrlDecode(str) {
|
|
|
736
736
|
|
|
737
737
|
// src/oauth/email-fetcher.ts
|
|
738
738
|
async function fetchUserEmail(provider, tokenData) {
|
|
739
|
+
const fetcher = EMAIL_FETCHERS[provider.toLowerCase()];
|
|
740
|
+
if (!fetcher) {
|
|
741
|
+
return tokenData.email;
|
|
742
|
+
}
|
|
739
743
|
try {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
return await fetchGitHubEmail(tokenData.accessToken);
|
|
743
|
-
case "gmail":
|
|
744
|
-
case "google":
|
|
745
|
-
return await fetchGoogleEmail(tokenData.accessToken);
|
|
746
|
-
case "notion":
|
|
747
|
-
return await fetchNotionEmail(tokenData.accessToken);
|
|
748
|
-
default:
|
|
749
|
-
return tokenData.email;
|
|
750
|
-
}
|
|
744
|
+
const email = await fetcher(tokenData);
|
|
745
|
+
return email ?? tokenData.email;
|
|
751
746
|
} catch (error) {
|
|
752
747
|
logger3.error(`Failed to fetch email for ${provider}:`, error);
|
|
753
|
-
return;
|
|
748
|
+
return tokenData.email;
|
|
754
749
|
}
|
|
755
750
|
}
|
|
756
|
-
async function fetchGitHubEmail(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
751
|
+
async function fetchGitHubEmail(token) {
|
|
752
|
+
const headers = {
|
|
753
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
754
|
+
Accept: "application/vnd.github.v3+json"
|
|
755
|
+
};
|
|
756
|
+
const userResponse = await fetch("https://api.github.com/user", { headers });
|
|
757
|
+
if (!userResponse.ok)
|
|
758
|
+
return;
|
|
759
|
+
const user = await userResponse.json();
|
|
760
|
+
if (user.email)
|
|
761
|
+
return user.email;
|
|
762
|
+
const emailsResponse = await fetch("https://api.github.com/user/emails", { headers });
|
|
763
|
+
if (!emailsResponse.ok)
|
|
764
|
+
return;
|
|
765
|
+
const emails = await emailsResponse.json();
|
|
766
|
+
const primary = emails.find((e) => e.primary && e.verified);
|
|
767
|
+
if (primary)
|
|
768
|
+
return primary.email;
|
|
769
|
+
const verified = emails.find((e) => e.verified);
|
|
770
|
+
if (verified)
|
|
771
|
+
return verified.email;
|
|
772
|
+
return emails[0]?.email;
|
|
773
|
+
}
|
|
774
|
+
async function fetchGoogleEmail(token) {
|
|
775
|
+
const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
|
|
776
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
777
|
+
});
|
|
778
|
+
if (!response.ok)
|
|
779
|
+
return;
|
|
780
|
+
const user = await response.json();
|
|
781
|
+
return user.email;
|
|
782
|
+
}
|
|
783
|
+
async function fetchNotionEmail(token) {
|
|
784
|
+
const response = await fetch("https://api.notion.com/v1/users/me", {
|
|
785
|
+
headers: {
|
|
786
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
787
|
+
"Notion-Version": "2022-06-28"
|
|
784
788
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
789
|
+
});
|
|
790
|
+
if (!response.ok)
|
|
791
|
+
return;
|
|
792
|
+
const user = await response.json();
|
|
793
|
+
return user.person?.email ?? user.bot?.owner?.user?.person?.email;
|
|
794
|
+
}
|
|
795
|
+
async function fetchLinearEmail(token) {
|
|
796
|
+
const response = await fetch("https://api.linear.app/graphql", {
|
|
797
|
+
method: "POST",
|
|
798
|
+
headers: {
|
|
799
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
800
|
+
"Content-Type": "application/json"
|
|
801
|
+
},
|
|
802
|
+
body: JSON.stringify({ query: "{ viewer { email } }" })
|
|
803
|
+
});
|
|
804
|
+
if (!response.ok)
|
|
805
|
+
return;
|
|
806
|
+
const body = await response.json();
|
|
807
|
+
return body.data?.viewer?.email;
|
|
808
|
+
}
|
|
809
|
+
async function fetchHubSpotEmail(token) {
|
|
810
|
+
const url = `https://api.hubapi.com/oauth/v1/access-tokens/${encodeURIComponent(token.accessToken)}`;
|
|
811
|
+
const response = await fetch(url);
|
|
812
|
+
if (!response.ok)
|
|
813
|
+
return;
|
|
814
|
+
const body = await response.json();
|
|
815
|
+
return body.user;
|
|
816
|
+
}
|
|
817
|
+
async function fetchPolarEmail(token) {
|
|
818
|
+
const response = await fetch("https://api.polar.sh/v1/oauth2/userinfo", {
|
|
819
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
820
|
+
});
|
|
821
|
+
if (!response.ok)
|
|
822
|
+
return;
|
|
823
|
+
const body = await response.json();
|
|
824
|
+
return body.email;
|
|
825
|
+
}
|
|
826
|
+
async function fetchTodoistEmail(token) {
|
|
827
|
+
const response = await fetch("https://api.todoist.com/sync/v9/sync", {
|
|
828
|
+
method: "POST",
|
|
829
|
+
headers: {
|
|
830
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
831
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
832
|
+
},
|
|
833
|
+
body: 'sync_token=*&resource_types=["user"]'
|
|
834
|
+
});
|
|
835
|
+
if (!response.ok)
|
|
836
|
+
return;
|
|
837
|
+
const body = await response.json();
|
|
838
|
+
return body.user?.email;
|
|
839
|
+
}
|
|
840
|
+
async function fetchVercelEmail(token) {
|
|
841
|
+
const response = await fetch("https://api.vercel.com/v2/user", {
|
|
842
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
843
|
+
});
|
|
844
|
+
if (!response.ok)
|
|
845
|
+
return;
|
|
846
|
+
const body = await response.json();
|
|
847
|
+
return body.user?.email ?? body.email;
|
|
848
|
+
}
|
|
849
|
+
async function fetchSlackEmail(token) {
|
|
850
|
+
const response = await fetch("https://slack.com/api/users.identity", {
|
|
851
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
852
|
+
});
|
|
853
|
+
if (!response.ok)
|
|
854
|
+
return;
|
|
855
|
+
const body = await response.json();
|
|
856
|
+
if (!body.ok)
|
|
857
|
+
return;
|
|
858
|
+
return body.user?.email;
|
|
859
|
+
}
|
|
860
|
+
async function fetchIntercomEmail(token) {
|
|
861
|
+
const response = await fetch("https://api.intercom.io/me", {
|
|
862
|
+
headers: {
|
|
863
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
864
|
+
Accept: "application/json"
|
|
788
865
|
}
|
|
789
|
-
|
|
790
|
-
|
|
866
|
+
});
|
|
867
|
+
if (!response.ok)
|
|
868
|
+
return;
|
|
869
|
+
const body = await response.json();
|
|
870
|
+
return body.email;
|
|
871
|
+
}
|
|
872
|
+
async function fetchAtlassianEmail(token) {
|
|
873
|
+
const response = await fetch("https://api.atlassian.com/me", {
|
|
874
|
+
headers: {
|
|
875
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
876
|
+
Accept: "application/json"
|
|
791
877
|
}
|
|
878
|
+
});
|
|
879
|
+
if (!response.ok)
|
|
792
880
|
return;
|
|
793
|
-
|
|
794
|
-
|
|
881
|
+
const body = await response.json();
|
|
882
|
+
return body.email;
|
|
883
|
+
}
|
|
884
|
+
async function fetchZendeskEmail(token) {
|
|
885
|
+
const subdomain = token.providerConfig?.subdomain?.trim();
|
|
886
|
+
if (!subdomain)
|
|
795
887
|
return;
|
|
796
|
-
}
|
|
888
|
+
const response = await fetch(`https://${subdomain}.zendesk.com/api/v2/users/me.json`, {
|
|
889
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
890
|
+
});
|
|
891
|
+
if (!response.ok)
|
|
892
|
+
return;
|
|
893
|
+
const body = await response.json();
|
|
894
|
+
return body.user?.email;
|
|
797
895
|
}
|
|
798
|
-
async function
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
});
|
|
805
|
-
if (!response.ok) {
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
const user = await response.json();
|
|
809
|
-
return user.email;
|
|
810
|
-
} catch (error) {
|
|
811
|
-
logger3.error("Failed to fetch Google email:", error);
|
|
896
|
+
async function fetchAirtableEmail(token) {
|
|
897
|
+
const response = await fetch("https://api.airtable.com/v0/meta/whoami", {
|
|
898
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
899
|
+
});
|
|
900
|
+
if (!response.ok)
|
|
812
901
|
return;
|
|
813
|
-
|
|
902
|
+
const body = await response.json();
|
|
903
|
+
return body.email;
|
|
814
904
|
}
|
|
815
|
-
async function
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
905
|
+
async function fetchDiscordEmail(token) {
|
|
906
|
+
const response = await fetch("https://discord.com/api/users/@me", {
|
|
907
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
908
|
+
});
|
|
909
|
+
if (!response.ok)
|
|
910
|
+
return;
|
|
911
|
+
const body = await response.json();
|
|
912
|
+
return body.email;
|
|
913
|
+
}
|
|
914
|
+
async function fetchDropboxEmail(token) {
|
|
915
|
+
const response = await fetch("https://api.dropboxapi.com/2/users/get_current_account", {
|
|
916
|
+
method: "POST",
|
|
917
|
+
headers: {
|
|
918
|
+
Authorization: `Bearer ${token.accessToken}`
|
|
919
|
+
},
|
|
920
|
+
body: "null"
|
|
921
|
+
});
|
|
922
|
+
if (!response.ok)
|
|
923
|
+
return;
|
|
924
|
+
const body = await response.json();
|
|
925
|
+
return body.email;
|
|
926
|
+
}
|
|
927
|
+
async function fetchGitLabEmail(token) {
|
|
928
|
+
const response = await fetch("https://gitlab.com/api/v4/user", {
|
|
929
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
930
|
+
});
|
|
931
|
+
if (!response.ok)
|
|
932
|
+
return;
|
|
933
|
+
const body = await response.json();
|
|
934
|
+
return body.email;
|
|
935
|
+
}
|
|
936
|
+
async function fetchRedditEmail(token) {
|
|
937
|
+
const response = await fetch("https://oauth.reddit.com/api/v1/me", {
|
|
938
|
+
headers: {
|
|
939
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
940
|
+
"User-Agent": "integrate-sdk"
|
|
825
941
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
} catch (error) {
|
|
829
|
-
logger3.error("Failed to fetch Notion email:", error);
|
|
942
|
+
});
|
|
943
|
+
if (!response.ok)
|
|
830
944
|
return;
|
|
831
|
-
|
|
945
|
+
const body = await response.json();
|
|
946
|
+
return body.name;
|
|
947
|
+
}
|
|
948
|
+
async function fetchMicrosoftEmail(token) {
|
|
949
|
+
const response = await fetch("https://graph.microsoft.com/v1.0/me", {
|
|
950
|
+
headers: { Authorization: `Bearer ${token.accessToken}` }
|
|
951
|
+
});
|
|
952
|
+
if (!response.ok)
|
|
953
|
+
return;
|
|
954
|
+
const body = await response.json();
|
|
955
|
+
return body.mail ?? body.userPrincipalName;
|
|
832
956
|
}
|
|
833
|
-
var logger3;
|
|
957
|
+
var logger3, EMAIL_FETCHERS;
|
|
834
958
|
var init_email_fetcher = __esm(() => {
|
|
835
959
|
init_logger();
|
|
836
960
|
logger3 = createLogger("EmailFetcher");
|
|
961
|
+
EMAIL_FETCHERS = {
|
|
962
|
+
github: fetchGitHubEmail,
|
|
963
|
+
gmail: fetchGoogleEmail,
|
|
964
|
+
google: fetchGoogleEmail,
|
|
965
|
+
gcal: fetchGoogleEmail,
|
|
966
|
+
gdrive: fetchGoogleEmail,
|
|
967
|
+
gdocs: fetchGoogleEmail,
|
|
968
|
+
gsheets: fetchGoogleEmail,
|
|
969
|
+
gslides: fetchGoogleEmail,
|
|
970
|
+
gcontacts: fetchGoogleEmail,
|
|
971
|
+
gmeet: fetchGoogleEmail,
|
|
972
|
+
gchat: fetchGoogleEmail,
|
|
973
|
+
gtasks: fetchGoogleEmail,
|
|
974
|
+
ga4: fetchGoogleEmail,
|
|
975
|
+
youtube: fetchGoogleEmail,
|
|
976
|
+
notion: fetchNotionEmail,
|
|
977
|
+
linear: fetchLinearEmail,
|
|
978
|
+
hubspot: fetchHubSpotEmail,
|
|
979
|
+
polar: fetchPolarEmail,
|
|
980
|
+
todoist: fetchTodoistEmail,
|
|
981
|
+
vercel: fetchVercelEmail,
|
|
982
|
+
slack: fetchSlackEmail,
|
|
983
|
+
intercom: fetchIntercomEmail,
|
|
984
|
+
jira: fetchAtlassianEmail,
|
|
985
|
+
zendesk: fetchZendeskEmail,
|
|
986
|
+
airtable: fetchAirtableEmail,
|
|
987
|
+
discord: fetchDiscordEmail,
|
|
988
|
+
dropbox: fetchDropboxEmail,
|
|
989
|
+
gitlab: fetchGitLabEmail,
|
|
990
|
+
reddit: fetchRedditEmail,
|
|
991
|
+
outlook: fetchMicrosoftEmail,
|
|
992
|
+
teams: fetchMicrosoftEmail,
|
|
993
|
+
onedrive: fetchMicrosoftEmail,
|
|
994
|
+
sharepoint: fetchMicrosoftEmail,
|
|
995
|
+
excel: fetchMicrosoftEmail,
|
|
996
|
+
word: fetchMicrosoftEmail,
|
|
997
|
+
powerpoint: fetchMicrosoftEmail,
|
|
998
|
+
planner: fetchMicrosoftEmail
|
|
999
|
+
};
|
|
837
1000
|
});
|
|
838
1001
|
|
|
839
1002
|
// src/utils/concurrency.ts
|
|
@@ -3276,6 +3439,148 @@ class IndexedDBStorage {
|
|
|
3276
3439
|
|
|
3277
3440
|
// src/oauth/manager.ts
|
|
3278
3441
|
init_email_fetcher();
|
|
3442
|
+
|
|
3443
|
+
// src/oauth/refresh.ts
|
|
3444
|
+
class RefreshRejectedError extends Error {
|
|
3445
|
+
provider;
|
|
3446
|
+
constructor(provider, message) {
|
|
3447
|
+
super(message ?? `OAuth refresh rejected (invalid_grant) for ${provider}`);
|
|
3448
|
+
this.name = "RefreshRejectedError";
|
|
3449
|
+
this.provider = provider;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
class RefreshTransientError extends Error {
|
|
3454
|
+
provider;
|
|
3455
|
+
constructor(provider, message) {
|
|
3456
|
+
super(message);
|
|
3457
|
+
this.name = "RefreshTransientError";
|
|
3458
|
+
this.provider = provider;
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
var DEFAULT_REFRESH_WINDOW_MS = 2 * 60 * 1000;
|
|
3462
|
+
function shouldRefreshToken(tokenData, windowMs = DEFAULT_REFRESH_WINDOW_MS) {
|
|
3463
|
+
if (!tokenData || !tokenData.refreshToken || !tokenData.expiresAt) {
|
|
3464
|
+
return false;
|
|
3465
|
+
}
|
|
3466
|
+
const expiresAtMs = Date.parse(tokenData.expiresAt);
|
|
3467
|
+
if (Number.isNaN(expiresAtMs)) {
|
|
3468
|
+
return false;
|
|
3469
|
+
}
|
|
3470
|
+
return expiresAtMs - Date.now() <= windowMs;
|
|
3471
|
+
}
|
|
3472
|
+
async function refreshViaMcp(opts) {
|
|
3473
|
+
const url = new URL("/oauth/refresh", opts.serverUrl);
|
|
3474
|
+
const body = {
|
|
3475
|
+
provider: opts.provider,
|
|
3476
|
+
refresh_token: opts.refreshToken,
|
|
3477
|
+
client_id: opts.clientId
|
|
3478
|
+
};
|
|
3479
|
+
if (opts.clientSecret) {
|
|
3480
|
+
body.client_secret = opts.clientSecret;
|
|
3481
|
+
}
|
|
3482
|
+
if (opts.subdomain) {
|
|
3483
|
+
body.subdomain = opts.subdomain;
|
|
3484
|
+
}
|
|
3485
|
+
if (opts.extraConfig) {
|
|
3486
|
+
for (const [key, value] of Object.entries(opts.extraConfig)) {
|
|
3487
|
+
if (body[key] === undefined) {
|
|
3488
|
+
body[key] = value;
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
const headers = { "Content-Type": "application/json" };
|
|
3493
|
+
if (opts.apiKey) {
|
|
3494
|
+
headers["X-API-KEY"] = opts.apiKey;
|
|
3495
|
+
}
|
|
3496
|
+
let response;
|
|
3497
|
+
try {
|
|
3498
|
+
response = await fetch(url.toString(), {
|
|
3499
|
+
method: "POST",
|
|
3500
|
+
headers,
|
|
3501
|
+
body: JSON.stringify(body),
|
|
3502
|
+
signal: opts.signal
|
|
3503
|
+
});
|
|
3504
|
+
} catch (err) {
|
|
3505
|
+
throw new RefreshTransientError(opts.provider, `Network error refreshing ${opts.provider} token: ${err.message}`);
|
|
3506
|
+
}
|
|
3507
|
+
if (response.status === 401) {
|
|
3508
|
+
let parsed = {};
|
|
3509
|
+
try {
|
|
3510
|
+
parsed = await response.json();
|
|
3511
|
+
} catch {}
|
|
3512
|
+
if (parsed.error === "invalid_grant") {
|
|
3513
|
+
throw new RefreshRejectedError(opts.provider);
|
|
3514
|
+
}
|
|
3515
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned 401`);
|
|
3516
|
+
}
|
|
3517
|
+
if (!response.ok) {
|
|
3518
|
+
let text = "";
|
|
3519
|
+
try {
|
|
3520
|
+
text = await response.text();
|
|
3521
|
+
} catch {}
|
|
3522
|
+
throw new RefreshTransientError(opts.provider, `Refresh endpoint returned ${response.status}: ${text || "<no body>"}`);
|
|
3523
|
+
}
|
|
3524
|
+
let result;
|
|
3525
|
+
try {
|
|
3526
|
+
result = await response.json();
|
|
3527
|
+
} catch (err) {
|
|
3528
|
+
throw new RefreshTransientError(opts.provider, `Malformed refresh response: ${err.message}`);
|
|
3529
|
+
}
|
|
3530
|
+
if (!result.accessToken) {
|
|
3531
|
+
throw new RefreshTransientError(opts.provider, `Refresh response missing access_token`);
|
|
3532
|
+
}
|
|
3533
|
+
if (!result.refreshToken) {
|
|
3534
|
+
result.refreshToken = opts.refreshToken;
|
|
3535
|
+
}
|
|
3536
|
+
return result;
|
|
3537
|
+
}
|
|
3538
|
+
async function resolveAccessToken(opts) {
|
|
3539
|
+
const { provider, currentTokens, force = false } = opts;
|
|
3540
|
+
const needsRefresh = force || shouldRefreshToken(currentTokens, opts.windowMs);
|
|
3541
|
+
if (!needsRefresh || !currentTokens.refreshToken) {
|
|
3542
|
+
return currentTokens.accessToken;
|
|
3543
|
+
}
|
|
3544
|
+
try {
|
|
3545
|
+
const refreshed = await refreshViaMcp({
|
|
3546
|
+
provider,
|
|
3547
|
+
refreshToken: currentTokens.refreshToken,
|
|
3548
|
+
clientId: opts.providerOAuth.clientId,
|
|
3549
|
+
clientSecret: opts.providerOAuth.clientSecret,
|
|
3550
|
+
subdomain: opts.providerOAuth.subdomain,
|
|
3551
|
+
extraConfig: opts.providerOAuth.extraConfig,
|
|
3552
|
+
serverUrl: opts.serverUrl,
|
|
3553
|
+
apiKey: opts.apiKey
|
|
3554
|
+
});
|
|
3555
|
+
const updated = {
|
|
3556
|
+
...currentTokens,
|
|
3557
|
+
accessToken: refreshed.accessToken,
|
|
3558
|
+
refreshToken: refreshed.refreshToken ?? currentTokens.refreshToken,
|
|
3559
|
+
tokenType: refreshed.tokenType || currentTokens.tokenType,
|
|
3560
|
+
expiresIn: refreshed.expiresIn ?? currentTokens.expiresIn,
|
|
3561
|
+
expiresAt: refreshed.expiresAt ?? currentTokens.expiresAt,
|
|
3562
|
+
scopes: refreshed.scopes && refreshed.scopes.length > 0 ? refreshed.scopes : currentTokens.scopes
|
|
3563
|
+
};
|
|
3564
|
+
if (opts.setProviderToken) {
|
|
3565
|
+
try {
|
|
3566
|
+
await opts.setProviderToken(provider, updated, updated.email, opts.context);
|
|
3567
|
+
} catch {}
|
|
3568
|
+
}
|
|
3569
|
+
return updated.accessToken;
|
|
3570
|
+
} catch (err) {
|
|
3571
|
+
if (err instanceof RefreshRejectedError) {
|
|
3572
|
+
if (opts.setProviderToken) {
|
|
3573
|
+
try {
|
|
3574
|
+
await opts.setProviderToken(provider, null, currentTokens.email, opts.context);
|
|
3575
|
+
} catch {}
|
|
3576
|
+
}
|
|
3577
|
+
throw err;
|
|
3578
|
+
}
|
|
3579
|
+
return currentTokens.accessToken;
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
// src/oauth/manager.ts
|
|
3279
3584
|
init_logger();
|
|
3280
3585
|
var logger4 = createLogger("OAuth");
|
|
3281
3586
|
|
|
@@ -3291,7 +3596,10 @@ class OAuthManager {
|
|
|
3291
3596
|
removeTokenCallback;
|
|
3292
3597
|
indexedDBStorage;
|
|
3293
3598
|
skipLocalStorage = false;
|
|
3294
|
-
|
|
3599
|
+
providerOAuth = {};
|
|
3600
|
+
mcpServerUrl;
|
|
3601
|
+
mcpApiKey;
|
|
3602
|
+
constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks, refreshConfig) {
|
|
3295
3603
|
this.oauthApiBase = oauthApiBase;
|
|
3296
3604
|
this.apiBaseUrl = apiBaseUrl;
|
|
3297
3605
|
this.windowManager = new OAuthWindowManager;
|
|
@@ -3303,6 +3611,9 @@ class OAuthManager {
|
|
|
3303
3611
|
this.getTokenCallback = tokenCallbacks?.getProviderToken;
|
|
3304
3612
|
this.setTokenCallback = tokenCallbacks?.setProviderToken;
|
|
3305
3613
|
this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
|
|
3614
|
+
this.providerOAuth = refreshConfig?.providers ?? {};
|
|
3615
|
+
this.mcpServerUrl = refreshConfig?.mcpServerUrl;
|
|
3616
|
+
this.mcpApiKey = refreshConfig?.apiKey;
|
|
3306
3617
|
this.indexedDBStorage = new IndexedDBStorage;
|
|
3307
3618
|
this.cleanupExpiredPendingAuths();
|
|
3308
3619
|
}
|
|
@@ -3512,7 +3823,9 @@ class OAuthManager {
|
|
|
3512
3823
|
try {
|
|
3513
3824
|
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
3514
3825
|
if (tokenData) {
|
|
3515
|
-
this.
|
|
3826
|
+
const refreshed = await this.maybeRefreshTokenData(provider, tokenData, context);
|
|
3827
|
+
this.providerTokens.set(provider, refreshed);
|
|
3828
|
+
return refreshed;
|
|
3516
3829
|
}
|
|
3517
3830
|
return tokenData;
|
|
3518
3831
|
} catch (error) {
|
|
@@ -3552,6 +3865,59 @@ class OAuthManager {
|
|
|
3552
3865
|
}
|
|
3553
3866
|
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
3554
3867
|
}
|
|
3868
|
+
configureTokenRefresh(refreshConfig) {
|
|
3869
|
+
if (refreshConfig.providers) {
|
|
3870
|
+
this.providerOAuth = refreshConfig.providers;
|
|
3871
|
+
}
|
|
3872
|
+
if (refreshConfig.mcpServerUrl !== undefined) {
|
|
3873
|
+
this.mcpServerUrl = refreshConfig.mcpServerUrl;
|
|
3874
|
+
}
|
|
3875
|
+
if (refreshConfig.apiKey !== undefined) {
|
|
3876
|
+
this.mcpApiKey = refreshConfig.apiKey;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
async maybeRefreshTokenData(provider, tokenData, context) {
|
|
3880
|
+
const credentials = this.providerOAuth[provider];
|
|
3881
|
+
const serverUrl = this.mcpServerUrl;
|
|
3882
|
+
if (!credentials || !serverUrl) {
|
|
3883
|
+
return tokenData;
|
|
3884
|
+
}
|
|
3885
|
+
if (!shouldRefreshToken(tokenData)) {
|
|
3886
|
+
return tokenData;
|
|
3887
|
+
}
|
|
3888
|
+
try {
|
|
3889
|
+
const newAccessToken = await resolveAccessToken({
|
|
3890
|
+
provider,
|
|
3891
|
+
currentTokens: tokenData,
|
|
3892
|
+
providerOAuth: {
|
|
3893
|
+
clientId: credentials.clientId,
|
|
3894
|
+
clientSecret: credentials.clientSecret,
|
|
3895
|
+
subdomain: credentials.config?.subdomain
|
|
3896
|
+
},
|
|
3897
|
+
serverUrl,
|
|
3898
|
+
apiKey: this.mcpApiKey,
|
|
3899
|
+
setProviderToken: this.setTokenCallback,
|
|
3900
|
+
context
|
|
3901
|
+
});
|
|
3902
|
+
if (newAccessToken === tokenData.accessToken) {
|
|
3903
|
+
return tokenData;
|
|
3904
|
+
}
|
|
3905
|
+
if (this.getTokenCallback) {
|
|
3906
|
+
try {
|
|
3907
|
+
const reloaded = await this.getTokenCallback(provider, tokenData.email, context);
|
|
3908
|
+
if (reloaded) {
|
|
3909
|
+
return reloaded;
|
|
3910
|
+
}
|
|
3911
|
+
} catch {}
|
|
3912
|
+
}
|
|
3913
|
+
return { ...tokenData, accessToken: newAccessToken };
|
|
3914
|
+
} catch (err) {
|
|
3915
|
+
if (err instanceof RefreshRejectedError) {
|
|
3916
|
+
throw err;
|
|
3917
|
+
}
|
|
3918
|
+
return tokenData;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3555
3921
|
clearProviderToken(provider) {
|
|
3556
3922
|
this.providerTokens.delete(provider);
|
|
3557
3923
|
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
@@ -4008,10 +4374,26 @@ class MCPClientBase {
|
|
|
4008
4374
|
};
|
|
4009
4375
|
this.onReauthRequired = config.onReauthRequired;
|
|
4010
4376
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
4377
|
+
const refreshProviders = {};
|
|
4378
|
+
for (const integration of this.integrations) {
|
|
4379
|
+
const oauth = integration?.oauth;
|
|
4380
|
+
if (oauth?.clientId && oauth?.provider) {
|
|
4381
|
+
refreshProviders[oauth.provider] = {
|
|
4382
|
+
clientId: oauth.clientId,
|
|
4383
|
+
clientSecret: oauth.clientSecret,
|
|
4384
|
+
config: oauth.config
|
|
4385
|
+
};
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
const mcpServerUrl = config.serverUrl;
|
|
4011
4389
|
this.oauthManager = new OAuthManager(oauthApiBase, config.oauthFlow, this.apiBaseUrl, {
|
|
4012
4390
|
getProviderToken: config.getProviderToken,
|
|
4013
4391
|
setProviderToken: config.setProviderToken,
|
|
4014
4392
|
removeProviderToken: config.removeProviderToken
|
|
4393
|
+
}, {
|
|
4394
|
+
providers: refreshProviders,
|
|
4395
|
+
mcpServerUrl,
|
|
4396
|
+
apiKey: config.apiKey
|
|
4015
4397
|
});
|
|
4016
4398
|
this.setSessionToken(config.sessionToken || this.loadSessionTokenFromStorage());
|
|
4017
4399
|
for (const integration of this.integrations) {
|
|
@@ -15978,6 +16360,34 @@ function convertJsonSchemaToGoogleSchema(jsonSchema, TypeEnum) {
|
|
|
15978
16360
|
// src/server.ts
|
|
15979
16361
|
var SERVER_LOG_CONTEXT3 = "server";
|
|
15980
16362
|
var logger93 = createLogger("MCPServer", SERVER_LOG_CONTEXT3);
|
|
16363
|
+
async function refreshTokenIfNeeded(provider, tokenData, providers, config, context) {
|
|
16364
|
+
const credentials = providers[provider];
|
|
16365
|
+
if (!credentials || !config.serverUrl) {
|
|
16366
|
+
return tokenData.accessToken;
|
|
16367
|
+
}
|
|
16368
|
+
try {
|
|
16369
|
+
return await resolveAccessToken({
|
|
16370
|
+
provider,
|
|
16371
|
+
currentTokens: tokenData,
|
|
16372
|
+
providerOAuth: {
|
|
16373
|
+
clientId: credentials.clientId,
|
|
16374
|
+
clientSecret: credentials.clientSecret,
|
|
16375
|
+
subdomain: credentials.config?.subdomain
|
|
16376
|
+
},
|
|
16377
|
+
serverUrl: config.serverUrl,
|
|
16378
|
+
apiKey: config.apiKey,
|
|
16379
|
+
setProviderToken: config.setProviderToken,
|
|
16380
|
+
context
|
|
16381
|
+
});
|
|
16382
|
+
} catch (err) {
|
|
16383
|
+
if (err instanceof RefreshRejectedError) {
|
|
16384
|
+
logger93.warn(`[Token Refresh] Refresh token rejected for ${provider}; integration marked disconnected.`);
|
|
16385
|
+
} else {
|
|
16386
|
+
logger93.warn(`[Token Refresh] Failed to refresh ${provider} token: ${err.message}`);
|
|
16387
|
+
}
|
|
16388
|
+
return tokenData.accessToken;
|
|
16389
|
+
}
|
|
16390
|
+
}
|
|
15981
16391
|
var globalServerConfig = null;
|
|
15982
16392
|
var globalMCPHandler = null;
|
|
15983
16393
|
var codeVerifierStorage = new Map;
|
|
@@ -16229,7 +16639,8 @@ function createMCPServer(config) {
|
|
|
16229
16639
|
if (provider) {
|
|
16230
16640
|
const tokenData = await config.getProviderToken(provider, undefined, context2);
|
|
16231
16641
|
if (tokenData?.accessToken) {
|
|
16232
|
-
|
|
16642
|
+
const accessToken = await refreshTokenIfNeeded(provider, tokenData, providers, config, context2);
|
|
16643
|
+
authHeader = `Bearer ${accessToken}`;
|
|
16233
16644
|
}
|
|
16234
16645
|
}
|
|
16235
16646
|
} catch {}
|