integrate-sdk 0.8.26 → 0.8.28-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.
@@ -627,6 +627,277 @@ class OAuthWindowManager {
627
627
  }
628
628
  }
629
629
 
630
+ // ../oauth/indexeddb-storage.ts
631
+ class IndexedDBStorage {
632
+ db = null;
633
+ initPromise = null;
634
+ async init() {
635
+ if (this.db) {
636
+ return this.db;
637
+ }
638
+ if (this.initPromise) {
639
+ return this.initPromise;
640
+ }
641
+ this.initPromise = new Promise((resolve, reject) => {
642
+ if (typeof window === "undefined" || !window.indexedDB) {
643
+ reject(new Error("IndexedDB is not available in this environment"));
644
+ return;
645
+ }
646
+ const request = window.indexedDB.open(DB_NAME, DB_VERSION);
647
+ request.onerror = () => {
648
+ this.initPromise = null;
649
+ reject(new Error(`Failed to open IndexedDB: ${request.error?.message}`));
650
+ };
651
+ request.onsuccess = () => {
652
+ this.db = request.result;
653
+ this.initPromise = null;
654
+ resolve(this.db);
655
+ };
656
+ request.onupgradeneeded = (event) => {
657
+ const db = event.target.result;
658
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
659
+ const store = db.createObjectStore(STORE_NAME, { keyPath: "accountId" });
660
+ store.createIndex("provider", "provider", { unique: false });
661
+ store.createIndex("email", "email", { unique: false });
662
+ store.createIndex("provider_email", ["provider", "email"], { unique: true });
663
+ }
664
+ };
665
+ });
666
+ return this.initPromise;
667
+ }
668
+ generateAccountId(provider, email) {
669
+ const str = `${provider}:${email}`;
670
+ let hash = 0;
671
+ for (let i = 0;i < str.length; i++) {
672
+ const char = str.charCodeAt(i);
673
+ hash = (hash << 5) - hash + char;
674
+ hash = hash & hash;
675
+ }
676
+ return `${provider}_${Math.abs(hash).toString(36)}`;
677
+ }
678
+ async saveToken(provider, email, tokenData) {
679
+ const db = await this.init();
680
+ const accountId = this.generateAccountId(provider, email);
681
+ const now = Date.now();
682
+ const entry = {
683
+ provider,
684
+ email,
685
+ accountId,
686
+ tokenData,
687
+ createdAt: now,
688
+ updatedAt: now
689
+ };
690
+ return new Promise((resolve, reject) => {
691
+ const transaction = db.transaction([STORE_NAME], "readwrite");
692
+ const store = transaction.objectStore(STORE_NAME);
693
+ const getRequest = store.get(accountId);
694
+ getRequest.onsuccess = () => {
695
+ const existing = getRequest.result;
696
+ if (existing) {
697
+ entry.createdAt = existing.createdAt;
698
+ }
699
+ const putRequest = store.put(entry);
700
+ putRequest.onsuccess = () => resolve();
701
+ putRequest.onerror = () => reject(new Error(`Failed to save token: ${putRequest.error?.message}`));
702
+ };
703
+ getRequest.onerror = () => reject(new Error(`Failed to check existing token: ${getRequest.error?.message}`));
704
+ });
705
+ }
706
+ async getToken(provider, email) {
707
+ const db = await this.init();
708
+ const accountId = this.generateAccountId(provider, email);
709
+ return new Promise((resolve, reject) => {
710
+ const transaction = db.transaction([STORE_NAME], "readonly");
711
+ const store = transaction.objectStore(STORE_NAME);
712
+ const request = store.get(accountId);
713
+ request.onsuccess = () => {
714
+ const entry = request.result;
715
+ resolve(entry?.tokenData);
716
+ };
717
+ request.onerror = () => reject(new Error(`Failed to get token: ${request.error?.message}`));
718
+ });
719
+ }
720
+ async getTokensByProvider(provider) {
721
+ const db = await this.init();
722
+ const tokens = new Map;
723
+ return new Promise((resolve, reject) => {
724
+ const transaction = db.transaction([STORE_NAME], "readonly");
725
+ const store = transaction.objectStore(STORE_NAME);
726
+ const index = store.index("provider");
727
+ const request = index.getAll(provider);
728
+ request.onsuccess = () => {
729
+ const entries = request.result;
730
+ for (const entry of entries) {
731
+ tokens.set(entry.email, entry.tokenData);
732
+ }
733
+ resolve(tokens);
734
+ };
735
+ request.onerror = () => reject(new Error(`Failed to get tokens: ${request.error?.message}`));
736
+ });
737
+ }
738
+ async listAccounts(provider) {
739
+ const db = await this.init();
740
+ return new Promise((resolve, reject) => {
741
+ const transaction = db.transaction([STORE_NAME], "readonly");
742
+ const store = transaction.objectStore(STORE_NAME);
743
+ const index = store.index("provider");
744
+ const request = index.getAll(provider);
745
+ request.onsuccess = () => {
746
+ const entries = request.result;
747
+ const accounts = entries.map((entry) => ({
748
+ email: entry.email,
749
+ accountId: entry.accountId,
750
+ expiresAt: entry.tokenData.expiresAt,
751
+ scopes: entry.tokenData.scopes,
752
+ createdAt: entry.createdAt
753
+ }));
754
+ resolve(accounts);
755
+ };
756
+ request.onerror = () => reject(new Error(`Failed to list accounts: ${request.error?.message}`));
757
+ });
758
+ }
759
+ async deleteToken(provider, email) {
760
+ const db = await this.init();
761
+ const accountId = this.generateAccountId(provider, email);
762
+ return new Promise((resolve, reject) => {
763
+ const transaction = db.transaction([STORE_NAME], "readwrite");
764
+ const store = transaction.objectStore(STORE_NAME);
765
+ const request = store.delete(accountId);
766
+ request.onsuccess = () => resolve();
767
+ request.onerror = () => reject(new Error(`Failed to delete token: ${request.error?.message}`));
768
+ });
769
+ }
770
+ async deleteTokensByProvider(provider) {
771
+ const db = await this.init();
772
+ return new Promise((resolve, reject) => {
773
+ const transaction = db.transaction([STORE_NAME], "readwrite");
774
+ const store = transaction.objectStore(STORE_NAME);
775
+ const index = store.index("provider");
776
+ const request = index.getAll(provider);
777
+ request.onsuccess = () => {
778
+ const entries = request.result;
779
+ const deletePromises = entries.map((entry) => {
780
+ return new Promise((resolveDelete, rejectDelete) => {
781
+ const deleteRequest = store.delete(entry.accountId);
782
+ deleteRequest.onsuccess = () => resolveDelete();
783
+ deleteRequest.onerror = () => rejectDelete(deleteRequest.error);
784
+ });
785
+ });
786
+ Promise.all(deletePromises).then(() => resolve()).catch((error) => reject(error));
787
+ };
788
+ request.onerror = () => reject(new Error(`Failed to delete tokens: ${request.error?.message}`));
789
+ });
790
+ }
791
+ async clearAll() {
792
+ const db = await this.init();
793
+ return new Promise((resolve, reject) => {
794
+ const transaction = db.transaction([STORE_NAME], "readwrite");
795
+ const store = transaction.objectStore(STORE_NAME);
796
+ const request = store.clear();
797
+ request.onsuccess = () => resolve();
798
+ request.onerror = () => reject(new Error(`Failed to clear tokens: ${request.error?.message}`));
799
+ });
800
+ }
801
+ }
802
+ var DB_NAME = "integrate_oauth_tokens", DB_VERSION = 1, STORE_NAME = "tokens";
803
+
804
+ // ../oauth/email-fetcher.ts
805
+ async function fetchUserEmail(provider, tokenData) {
806
+ try {
807
+ switch (provider.toLowerCase()) {
808
+ case "github":
809
+ return await fetchGitHubEmail(tokenData.accessToken);
810
+ case "gmail":
811
+ case "google":
812
+ return await fetchGoogleEmail(tokenData.accessToken);
813
+ case "notion":
814
+ return await fetchNotionEmail(tokenData.accessToken);
815
+ default:
816
+ return tokenData.email;
817
+ }
818
+ } catch (error) {
819
+ console.error(`Failed to fetch email for ${provider}:`, error);
820
+ return;
821
+ }
822
+ }
823
+ async function fetchGitHubEmail(accessToken) {
824
+ try {
825
+ const userResponse = await fetch("https://api.github.com/user", {
826
+ headers: {
827
+ Authorization: `Bearer ${accessToken}`,
828
+ Accept: "application/vnd.github.v3+json"
829
+ }
830
+ });
831
+ if (!userResponse.ok) {
832
+ return;
833
+ }
834
+ const user = await userResponse.json();
835
+ if (user.email) {
836
+ return user.email;
837
+ }
838
+ const emailsResponse = await fetch("https://api.github.com/user/emails", {
839
+ headers: {
840
+ Authorization: `Bearer ${accessToken}`,
841
+ Accept: "application/vnd.github.v3+json"
842
+ }
843
+ });
844
+ if (!emailsResponse.ok) {
845
+ return;
846
+ }
847
+ const emails = await emailsResponse.json();
848
+ const primaryEmail = emails.find((e) => e.primary && e.verified);
849
+ if (primaryEmail) {
850
+ return primaryEmail.email;
851
+ }
852
+ const verifiedEmail = emails.find((e) => e.verified);
853
+ if (verifiedEmail) {
854
+ return verifiedEmail.email;
855
+ }
856
+ if (emails.length > 0 && emails[0]?.email) {
857
+ return emails[0].email;
858
+ }
859
+ return;
860
+ } catch (error) {
861
+ console.error("Failed to fetch GitHub email:", error);
862
+ return;
863
+ }
864
+ }
865
+ async function fetchGoogleEmail(accessToken) {
866
+ try {
867
+ const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
868
+ headers: {
869
+ Authorization: `Bearer ${accessToken}`
870
+ }
871
+ });
872
+ if (!response.ok) {
873
+ return;
874
+ }
875
+ const user = await response.json();
876
+ return user.email;
877
+ } catch (error) {
878
+ console.error("Failed to fetch Google email:", error);
879
+ return;
880
+ }
881
+ }
882
+ async function fetchNotionEmail(accessToken) {
883
+ try {
884
+ const response = await fetch("https://api.notion.com/v1/users/me", {
885
+ headers: {
886
+ Authorization: `Bearer ${accessToken}`,
887
+ "Notion-Version": "2022-06-28"
888
+ }
889
+ });
890
+ if (!response.ok) {
891
+ return;
892
+ }
893
+ const user = await response.json();
894
+ return user.person?.email;
895
+ } catch (error) {
896
+ console.error("Failed to fetch Notion email:", error);
897
+ return;
898
+ }
899
+ }
900
+
630
901
  // ../oauth/manager.ts
631
902
  class OAuthManager {
632
903
  pendingAuths = new Map;
@@ -638,7 +909,8 @@ class OAuthManager {
638
909
  getTokenCallback;
639
910
  setTokenCallback;
640
911
  removeTokenCallback;
641
- skipLocalStorage;
912
+ indexedDBStorage;
913
+ skipLocalStorage = false;
642
914
  constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks) {
643
915
  this.oauthApiBase = oauthApiBase;
644
916
  this.apiBaseUrl = apiBaseUrl;
@@ -651,7 +923,7 @@ class OAuthManager {
651
923
  this.getTokenCallback = tokenCallbacks?.getProviderToken;
652
924
  this.setTokenCallback = tokenCallbacks?.setProviderToken;
653
925
  this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
654
- this.skipLocalStorage = !!(tokenCallbacks?.getProviderToken || tokenCallbacks?.setProviderToken);
926
+ this.indexedDBStorage = new IndexedDBStorage;
655
927
  this.cleanupExpiredPendingAuths();
656
928
  }
657
929
  async initiateFlow(provider, config, returnUrl) {
@@ -669,13 +941,10 @@ class OAuthManager {
669
941
  };
670
942
  this.pendingAuths.set(state, pendingAuth);
671
943
  this.savePendingAuthToStorage(state, pendingAuth);
672
- console.log("[OAuth] Requesting authorization URL, flow mode:", this.flowConfig.mode);
673
944
  const authUrl = await this.getAuthorizationUrl(provider, state, codeChallenge, config.redirectUri, codeVerifier);
674
- console.log("[OAuth] Received authorization URL, length:", authUrl?.length);
675
945
  if (!authUrl || authUrl.trim() === "") {
676
946
  throw new Error("Received empty authorization URL from server");
677
947
  }
678
- console.log("[OAuth] Opening authorization URL, mode:", this.flowConfig.mode);
679
948
  if (this.flowConfig.mode === "popup") {
680
949
  this.windowManager.openPopup(authUrl, this.flowConfig.popupOptions);
681
950
  try {
@@ -686,7 +955,6 @@ class OAuthManager {
686
955
  throw error;
687
956
  }
688
957
  } else {
689
- console.log("[OAuth] Redirecting to authorization URL:", authUrl.substring(0, 100) + (authUrl.length > 100 ? "..." : ""));
690
958
  this.windowManager.openRedirect(authUrl);
691
959
  }
692
960
  }
@@ -713,8 +981,12 @@ class OAuthManager {
713
981
  expiresAt: tokenData.expiresAt,
714
982
  scopes: tokenData.scopes
715
983
  };
984
+ const email = await fetchUserEmail(provider, tokenDataToStore);
985
+ if (email) {
986
+ tokenDataToStore.email = email;
987
+ }
716
988
  this.providerTokens.set(provider, tokenDataToStore);
717
- await this.saveProviderToken(provider, tokenDataToStore);
989
+ await this.saveProviderToken(provider, tokenDataToStore, email);
718
990
  this.pendingAuths.delete(state);
719
991
  this.removePendingAuthFromStorage(state);
720
992
  return { ...tokenDataToStore, provider };
@@ -750,8 +1022,12 @@ class OAuthManager {
750
1022
  expiresAt: response.expiresAt,
751
1023
  scopes: response.scopes
752
1024
  };
1025
+ const email = await fetchUserEmail(pendingAuth.provider, tokenData);
1026
+ if (email) {
1027
+ tokenData.email = email;
1028
+ }
753
1029
  this.providerTokens.set(pendingAuth.provider, tokenData);
754
- await this.saveProviderToken(pendingAuth.provider, tokenData);
1030
+ await this.saveProviderToken(pendingAuth.provider, tokenData, email);
755
1031
  this.pendingAuths.delete(state);
756
1032
  this.removePendingAuthFromStorage(state);
757
1033
  return { ...tokenData, provider: pendingAuth.provider };
@@ -761,8 +1037,8 @@ class OAuthManager {
761
1037
  throw error;
762
1038
  }
763
1039
  }
764
- async checkAuthStatus(provider) {
765
- const tokenData = await this.getProviderToken(provider);
1040
+ async checkAuthStatus(provider, email) {
1041
+ const tokenData = await this.getProviderToken(provider, email);
766
1042
  if (!tokenData) {
767
1043
  return {
768
1044
  authorized: false,
@@ -776,30 +1052,83 @@ class OAuthManager {
776
1052
  expiresAt: tokenData.expiresAt
777
1053
  };
778
1054
  }
1055
+ async disconnectAccount(provider, email, context) {
1056
+ if (this.removeTokenCallback) {
1057
+ try {
1058
+ await this.removeTokenCallback(provider, email, context);
1059
+ } catch (error) {
1060
+ console.error(`Failed to delete token for ${provider} (${email}) from database:`, error);
1061
+ }
1062
+ } else if (this.setTokenCallback) {
1063
+ try {
1064
+ await this.setTokenCallback(provider, null, email, context);
1065
+ } catch (error) {
1066
+ console.error(`Failed to delete token for ${provider} (${email}) from database:`, error);
1067
+ }
1068
+ }
1069
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1070
+ try {
1071
+ await this.indexedDBStorage.deleteToken(provider, email);
1072
+ } catch (error) {
1073
+ console.error(`Failed to delete token from IndexedDB for ${provider} (${email}):`, error);
1074
+ }
1075
+ }
1076
+ this.providerTokens.delete(provider);
1077
+ }
779
1078
  async disconnectProvider(provider, context) {
780
1079
  if (this.removeTokenCallback) {
781
1080
  try {
782
- await this.removeTokenCallback(provider, context);
1081
+ await this.removeTokenCallback(provider, undefined, context);
783
1082
  } catch (error) {
784
- console.error(`Failed to delete token for ${provider} from database via removeProviderToken:`, error);
1083
+ console.error(`Failed to delete tokens for ${provider} from database:`, error);
785
1084
  }
786
1085
  } else if (this.setTokenCallback) {
787
1086
  try {
788
- const tokenData = await this.getProviderToken(provider, context);
789
- if (tokenData) {
790
- await this.setTokenCallback(provider, null, context);
791
- }
1087
+ await this.setTokenCallback(provider, null, undefined, context);
1088
+ } catch (error) {
1089
+ console.error(`Failed to delete tokens for ${provider} from database:`, error);
1090
+ }
1091
+ }
1092
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1093
+ try {
1094
+ await this.indexedDBStorage.deleteTokensByProvider(provider);
792
1095
  } catch (error) {
793
- console.error(`Failed to delete token for ${provider} from database via setProviderToken:`, error);
1096
+ if (typeof window !== "undefined" && window.localStorage) {
1097
+ try {
1098
+ const key = `integrate_token_${provider}`;
1099
+ window.localStorage.removeItem(key);
1100
+ } catch (localStorageError) {}
1101
+ }
1102
+ }
1103
+ }
1104
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1105
+ if (typeof window !== "undefined" && window.localStorage) {
1106
+ try {
1107
+ const key = `integrate_token_${provider}`;
1108
+ window.localStorage.removeItem(key);
1109
+ } catch (localStorageError) {}
794
1110
  }
795
1111
  }
796
1112
  this.providerTokens.delete(provider);
797
- this.clearProviderToken(provider);
798
1113
  }
799
- async getProviderToken(provider, context) {
1114
+ async listAccounts(provider) {
1115
+ if (this.getTokenCallback) {
1116
+ return [];
1117
+ }
1118
+ if (!this.getTokenCallback) {
1119
+ try {
1120
+ return await this.indexedDBStorage.listAccounts(provider);
1121
+ } catch (error) {
1122
+ console.error(`Failed to list accounts for ${provider}:`, error);
1123
+ return [];
1124
+ }
1125
+ }
1126
+ return [];
1127
+ }
1128
+ async getProviderToken(provider, email, context) {
800
1129
  if (this.getTokenCallback) {
801
1130
  try {
802
- const tokenData = await this.getTokenCallback(provider, context);
1131
+ const tokenData = await this.getTokenCallback(provider, email, context);
803
1132
  if (tokenData) {
804
1133
  this.providerTokens.set(provider, tokenData);
805
1134
  }
@@ -809,6 +1138,17 @@ class OAuthManager {
809
1138
  return;
810
1139
  }
811
1140
  }
1141
+ if (email && !this.getTokenCallback) {
1142
+ try {
1143
+ const tokenData = await this.indexedDBStorage.getToken(provider, email);
1144
+ if (tokenData) {
1145
+ this.providerTokens.set(provider, tokenData);
1146
+ }
1147
+ return tokenData;
1148
+ } catch (error) {
1149
+ console.error(`Failed to get token from IndexedDB for ${provider}:`, error);
1150
+ }
1151
+ }
812
1152
  return this.providerTokens.get(provider);
813
1153
  }
814
1154
  getAllProviderTokens() {
@@ -817,35 +1157,45 @@ class OAuthManager {
817
1157
  getProviderTokenFromCache(provider) {
818
1158
  return this.providerTokens.get(provider);
819
1159
  }
820
- async setProviderToken(provider, tokenData, context) {
1160
+ async setProviderToken(provider, tokenData, email, context) {
1161
+ const tokenEmail = email || tokenData?.email;
821
1162
  if (tokenData === null) {
822
- this.providerTokens.delete(provider);
1163
+ if (tokenEmail) {
1164
+ this.providerTokens.delete(provider);
1165
+ } else {
1166
+ this.providerTokens.delete(provider);
1167
+ }
823
1168
  } else {
824
1169
  this.providerTokens.set(provider, tokenData);
825
1170
  }
826
- await this.saveProviderToken(provider, tokenData, context);
1171
+ await this.saveProviderToken(provider, tokenData, tokenEmail, context);
827
1172
  }
828
1173
  clearProviderToken(provider) {
829
1174
  this.providerTokens.delete(provider);
830
- if (!this.skipLocalStorage && typeof window !== "undefined" && window.localStorage) {
831
- try {
832
- window.localStorage.removeItem(`integrate_token_${provider}`);
833
- } catch (error) {
834
- console.error(`Failed to clear token for ${provider} from localStorage:`, error);
835
- }
1175
+ if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
1176
+ this.indexedDBStorage.deleteTokensByProvider(provider).catch((error) => {
1177
+ console.error(`Failed to clear tokens for ${provider} from IndexedDB:`, error);
1178
+ });
836
1179
  }
837
1180
  }
838
1181
  clearAllProviderTokens() {
839
- const providers = Array.from(this.providerTokens.keys());
840
1182
  this.providerTokens.clear();
841
- if (!this.skipLocalStorage && typeof window !== "undefined" && window.localStorage) {
842
- for (const provider of providers) {
1183
+ if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
1184
+ if (typeof window !== "undefined" && window.localStorage) {
843
1185
  try {
844
- window.localStorage.removeItem(`integrate_token_${provider}`);
845
- } catch (error) {
846
- console.error(`Failed to clear token for ${provider} from localStorage:`, error);
847
- }
1186
+ const keysToRemove = [];
1187
+ for (let i = 0;i < window.localStorage.length; i++) {
1188
+ const key = window.localStorage.key(i);
1189
+ if (key && key.startsWith("integrate_token_")) {
1190
+ keysToRemove.push(key);
1191
+ }
1192
+ }
1193
+ keysToRemove.forEach((key) => window.localStorage.removeItem(key));
1194
+ } catch (localStorageError) {}
848
1195
  }
1196
+ this.indexedDBStorage.clearAll().catch((error) => {
1197
+ console.error("Failed to clear all tokens from IndexedDB:", error);
1198
+ });
849
1199
  }
850
1200
  }
851
1201
  clearAllPendingAuths() {
@@ -866,67 +1216,125 @@ class OAuthManager {
866
1216
  }
867
1217
  }
868
1218
  }
869
- async saveProviderToken(provider, tokenData, context) {
1219
+ async saveProviderToken(provider, tokenData, email, context) {
870
1220
  if (this.setTokenCallback) {
871
1221
  try {
872
- await this.setTokenCallback(provider, tokenData, context);
1222
+ await this.setTokenCallback(provider, tokenData, email, context);
873
1223
  } catch (error) {
874
1224
  console.error(`Failed to ${tokenData === null ? "delete" : "save"} token for ${provider} via callback:`, error);
875
1225
  throw error;
876
1226
  }
877
1227
  return;
878
1228
  }
879
- if (tokenData === null) {
880
- this.clearProviderToken(provider);
1229
+ if (this.skipLocalStorage) {
881
1230
  return;
882
1231
  }
883
- if (this.skipLocalStorage) {
884
- console.log(`[OAuth] Token for ${provider} stored in memory only (skipLocalStorage: true). Configure setProviderToken/getProviderToken callbacks for persistence.`);
1232
+ if (tokenData === null) {
1233
+ if (email) {
1234
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1235
+ await this.indexedDBStorage.deleteToken(provider, email).catch(() => {
1236
+ if (typeof window !== "undefined" && window.localStorage) {
1237
+ try {
1238
+ const key = `integrate_token_${provider}`;
1239
+ window.localStorage.removeItem(key);
1240
+ } catch (localStorageError) {}
1241
+ }
1242
+ });
1243
+ }
1244
+ } else {
1245
+ this.clearProviderToken(provider);
1246
+ if (typeof window !== "undefined" && window.localStorage) {
1247
+ try {
1248
+ const key = `integrate_token_${provider}`;
1249
+ window.localStorage.removeItem(key);
1250
+ } catch (localStorageError) {}
1251
+ }
1252
+ }
885
1253
  return;
886
1254
  }
887
- if (typeof window !== "undefined" && window.localStorage) {
1255
+ const tokenEmail = email || tokenData.email;
1256
+ if (tokenEmail) {
888
1257
  try {
889
- const key = `integrate_token_${provider}`;
890
- window.localStorage.setItem(key, JSON.stringify(tokenData));
1258
+ await this.indexedDBStorage.saveToken(provider, tokenEmail, tokenData);
891
1259
  } catch (error) {
892
- console.error(`Failed to save token for ${provider} to localStorage:`, error);
1260
+ if (typeof window !== "undefined" && window.localStorage) {
1261
+ try {
1262
+ const key = `integrate_token_${provider}`;
1263
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1264
+ } catch (localStorageError) {
1265
+ console.error(`Failed to save token for ${provider} to localStorage:`, localStorageError);
1266
+ }
1267
+ } else {
1268
+ console.error(`Failed to save token for ${provider} to IndexedDB:`, error);
1269
+ }
1270
+ }
1271
+ } else {
1272
+ if (typeof window !== "undefined" && window.localStorage) {
1273
+ try {
1274
+ const key = `integrate_token_${provider}`;
1275
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1276
+ } catch (localStorageError) {
1277
+ console.error(`Failed to save token for ${provider} to localStorage:`, localStorageError);
1278
+ }
893
1279
  }
894
1280
  }
895
1281
  }
896
- async loadProviderToken(provider) {
1282
+ async loadProviderToken(provider, email, context) {
897
1283
  if (this.getTokenCallback) {
898
1284
  try {
899
- return await this.getTokenCallback(provider);
1285
+ return await this.getTokenCallback(provider, email, context);
900
1286
  } catch (error) {
901
1287
  console.error(`Failed to load token for ${provider} via callback:`, error);
902
1288
  return;
903
1289
  }
904
1290
  }
905
1291
  if (this.skipLocalStorage) {
906
- return;
1292
+ return this.providerTokens.get(provider);
907
1293
  }
908
- if (typeof window !== "undefined" && window.localStorage) {
1294
+ if (email) {
909
1295
  try {
910
- const key = `integrate_token_${provider}`;
911
- const stored = window.localStorage.getItem(key);
912
- if (stored) {
913
- return JSON.parse(stored);
1296
+ return await this.indexedDBStorage.getToken(provider, email);
1297
+ } catch (error) {
1298
+ if (typeof window !== "undefined" && window.localStorage) {
1299
+ try {
1300
+ const key = `integrate_token_${provider}`;
1301
+ const stored = window.localStorage.getItem(key);
1302
+ if (stored) {
1303
+ return JSON.parse(stored);
1304
+ }
1305
+ } catch (localStorageError) {}
1306
+ }
1307
+ return;
1308
+ }
1309
+ } else {
1310
+ try {
1311
+ const tokens = await this.indexedDBStorage.getTokensByProvider(provider);
1312
+ if (tokens.size > 0) {
1313
+ return tokens.values().next().value;
914
1314
  }
915
1315
  } catch (error) {
916
- console.error(`Failed to load token for ${provider} from localStorage:`, error);
1316
+ if (typeof window !== "undefined" && window.localStorage) {
1317
+ try {
1318
+ const key = `integrate_token_${provider}`;
1319
+ const stored = window.localStorage.getItem(key);
1320
+ if (stored) {
1321
+ return JSON.parse(stored);
1322
+ }
1323
+ } catch (localStorageError) {}
1324
+ }
917
1325
  }
918
1326
  }
919
1327
  return;
920
1328
  }
921
1329
  async loadAllProviderTokens(providers) {
922
1330
  for (const provider of providers) {
923
- const tokenData = await this.loadProviderToken(provider);
1331
+ const tokenData = await this.loadProviderToken(provider, undefined, undefined);
924
1332
  if (tokenData) {
925
1333
  this.providerTokens.set(provider, tokenData);
926
1334
  }
927
1335
  }
928
1336
  }
929
- loadProviderTokenSync(provider) {
1337
+ loadAllProviderTokensSync(providers) {
930
1338
  if (this.getTokenCallback) {
931
1339
  return;
932
1340
  }
@@ -934,33 +1342,20 @@ class OAuthManager {
934
1342
  return;
935
1343
  }
936
1344
  if (typeof window !== "undefined" && window.localStorage) {
937
- try {
938
- const key = `integrate_token_${provider}`;
939
- const stored = window.localStorage.getItem(key);
940
- if (stored) {
941
- return JSON.parse(stored);
942
- }
943
- } catch (error) {
944
- console.error(`Failed to load token for ${provider} from localStorage:`, error);
945
- }
946
- }
947
- return;
948
- }
949
- loadAllProviderTokensSync(providers) {
950
- if (this.getTokenCallback) {
951
- return;
952
- }
953
- for (const provider of providers) {
954
- const tokenData = this.loadProviderTokenSync(provider);
955
- if (tokenData) {
956
- this.providerTokens.set(provider, tokenData);
1345
+ for (const provider of providers) {
1346
+ try {
1347
+ const key = `integrate_token_${provider}`;
1348
+ const stored = window.localStorage.getItem(key);
1349
+ if (stored) {
1350
+ const tokenData = JSON.parse(stored);
1351
+ this.providerTokens.set(provider, tokenData);
1352
+ }
1353
+ } catch {}
957
1354
  }
958
1355
  }
1356
+ this.loadAllProviderTokens(providers).catch(() => {});
959
1357
  }
960
1358
  savePendingAuthToStorage(state, pendingAuth) {
961
- if (this.skipLocalStorage) {
962
- return;
963
- }
964
1359
  if (typeof window !== "undefined" && window.localStorage) {
965
1360
  try {
966
1361
  const key = `integrate_oauth_pending_${state}`;
@@ -971,9 +1366,6 @@ class OAuthManager {
971
1366
  }
972
1367
  }
973
1368
  loadPendingAuthFromStorage(state) {
974
- if (this.skipLocalStorage) {
975
- return;
976
- }
977
1369
  if (typeof window !== "undefined" && window.localStorage) {
978
1370
  try {
979
1371
  const key = `integrate_oauth_pending_${state}`;
@@ -988,9 +1380,6 @@ class OAuthManager {
988
1380
  return;
989
1381
  }
990
1382
  removePendingAuthFromStorage(state) {
991
- if (this.skipLocalStorage) {
992
- return;
993
- }
994
1383
  if (typeof window !== "undefined" && window.localStorage) {
995
1384
  try {
996
1385
  const key = `integrate_oauth_pending_${state}`;
@@ -1001,9 +1390,6 @@ class OAuthManager {
1001
1390
  }
1002
1391
  }
1003
1392
  cleanupExpiredPendingAuths() {
1004
- if (this.skipLocalStorage) {
1005
- return;
1006
- }
1007
1393
  if (typeof window !== "undefined" && window.localStorage) {
1008
1394
  try {
1009
1395
  const prefix = "integrate_oauth_pending_";
@@ -1050,7 +1436,7 @@ class OAuthManager {
1050
1436
  })
1051
1437
  });
1052
1438
  if (response.headers.get("X-Integrate-Use-Database") === "true") {
1053
- this.skipLocalStorage = true;
1439
+ this.setSkipLocalStorage(true);
1054
1440
  }
1055
1441
  if (!response.ok) {
1056
1442
  const error = await response.text();
@@ -1058,15 +1444,11 @@ class OAuthManager {
1058
1444
  }
1059
1445
  const data = await response.json();
1060
1446
  if (!data.authorizationUrl) {
1061
- console.error("[OAuth] Authorization URL is missing from response:", data);
1062
1447
  throw new Error("Authorization URL is missing from server response");
1063
1448
  }
1064
1449
  if (typeof data.authorizationUrl !== "string" || data.authorizationUrl.trim() === "") {
1065
- console.error("[OAuth] Invalid authorization URL received:", data.authorizationUrl);
1066
1450
  throw new Error("Invalid authorization URL received from server");
1067
1451
  }
1068
- const urlPreview = data.authorizationUrl.length > 100 ? data.authorizationUrl.substring(0, 100) + "..." : data.authorizationUrl;
1069
- console.log("[OAuth] Received authorization URL from API route:", urlPreview);
1070
1452
  return data.authorizationUrl;
1071
1453
  }
1072
1454
  async exchangeCodeForToken(provider, code, codeVerifier, state) {
@@ -1084,7 +1466,7 @@ class OAuthManager {
1084
1466
  })
1085
1467
  });
1086
1468
  if (response.headers.get("X-Integrate-Use-Database") === "true") {
1087
- this.skipLocalStorage = true;
1469
+ this.setSkipLocalStorage(true);
1088
1470
  }
1089
1471
  if (!response.ok) {
1090
1472
  const error = await response.text();
@@ -1353,7 +1735,7 @@ class MCPClientBase {
1353
1735
  const hasApiKey = !!transportHeaders["X-API-KEY"];
1354
1736
  if (hasApiKey) {
1355
1737
  if (provider) {
1356
- const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
1738
+ const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
1357
1739
  if (tokenData && this.transport.setHeader) {
1358
1740
  const previousAuthHeader = transportHeaders["Authorization"];
1359
1741
  try {
@@ -1383,7 +1765,7 @@ class MCPClientBase {
1383
1765
  "Content-Type": "application/json"
1384
1766
  };
1385
1767
  if (provider) {
1386
- const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
1768
+ const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
1387
1769
  if (tokenData) {
1388
1770
  headers["Authorization"] = `Bearer ${tokenData.accessToken}`;
1389
1771
  }
@@ -1527,6 +1909,29 @@ class MCPClientBase {
1527
1909
  throw error;
1528
1910
  }
1529
1911
  }
1912
+ async disconnectAccount(provider, email, context) {
1913
+ const integration = this.integrations.find((p) => p.oauth?.provider === provider);
1914
+ if (!integration?.oauth) {
1915
+ throw new Error(`No OAuth configuration found for provider: ${provider}`);
1916
+ }
1917
+ try {
1918
+ await this.oauthManager.disconnectAccount(provider, email, context);
1919
+ const accounts = await this.oauthManager.listAccounts(provider);
1920
+ if (accounts.length === 0) {
1921
+ this.authState.set(provider, { authenticated: false });
1922
+ }
1923
+ this.eventEmitter.emit("auth:disconnect", { provider });
1924
+ } catch (error) {
1925
+ this.eventEmitter.emit("auth:error", {
1926
+ provider,
1927
+ error
1928
+ });
1929
+ throw error;
1930
+ }
1931
+ }
1932
+ async listAccounts(provider) {
1933
+ return await this.oauthManager.listAccounts(provider);
1934
+ }
1530
1935
  async logout() {
1531
1936
  this.clearSessionToken();
1532
1937
  this.oauthManager.clearAllPendingAuths();
@@ -1559,13 +1964,13 @@ class MCPClientBase {
1559
1964
  isProviderAuthenticated(provider) {
1560
1965
  return this.authState.get(provider)?.authenticated ?? false;
1561
1966
  }
1562
- async isAuthorized(provider, context) {
1967
+ async isAuthorized(provider, email, context) {
1563
1968
  if (this.oauthCallbackPromise) {
1564
1969
  await this.oauthCallbackPromise;
1565
1970
  this.oauthCallbackPromise = null;
1566
1971
  }
1567
1972
  try {
1568
- const tokenData = await this.oauthManager.getProviderToken(provider, context);
1973
+ const tokenData = await this.oauthManager.getProviderToken(provider, email, context);
1569
1974
  const isAuthenticated = !!tokenData;
1570
1975
  const currentState = this.authState.get(provider);
1571
1976
  if (currentState) {
@@ -1603,8 +2008,8 @@ class MCPClientBase {
1603
2008
  }
1604
2009
  return authorized;
1605
2010
  }
1606
- async getAuthorizationStatus(provider) {
1607
- return await this.oauthManager.checkAuthStatus(provider);
2011
+ async getAuthorizationStatus(provider, email) {
2012
+ return await this.oauthManager.checkAuthStatus(provider, email);
1608
2013
  }
1609
2014
  async authorize(provider, options) {
1610
2015
  const integration = this.integrations.find((p) => p.oauth?.provider === provider);
@@ -1662,11 +2067,11 @@ class MCPClientBase {
1662
2067
  throw error;
1663
2068
  }
1664
2069
  }
1665
- async getProviderToken(provider, context) {
1666
- return await this.oauthManager.getProviderToken(provider, context);
2070
+ async getProviderToken(provider, email, context) {
2071
+ return await this.oauthManager.getProviderToken(provider, email, context);
1667
2072
  }
1668
- async setProviderToken(provider, tokenData, context) {
1669
- await this.oauthManager.setProviderToken(provider, tokenData, context);
2073
+ async setProviderToken(provider, tokenData, email, context) {
2074
+ await this.oauthManager.setProviderToken(provider, tokenData, email, context);
1670
2075
  if (tokenData === null) {
1671
2076
  this.authState.set(provider, { authenticated: false });
1672
2077
  } else {
@@ -6708,7 +7113,6 @@ function createMCPServer(config) {
6708
7113
  }
6709
7114
  }
6710
7115
  if (!targetOrigin) {
6711
- console.warn("[OAuth] Could not determine frontend origin for redirect. Using request origin as fallback.");
6712
7116
  targetOrigin = new URL(webRequest.url).origin;
6713
7117
  }
6714
7118
  const targetUrl = new URL(returnUrl, targetOrigin);
@@ -7252,7 +7656,6 @@ class OAuthHandler {
7252
7656
  } else {
7253
7657
  authorizeRequest = request;
7254
7658
  }
7255
- console.log("[handleAuthorize] Received request for provider:", authorizeRequest.provider, "has codeVerifier:", !!authorizeRequest.codeVerifier, "has frontendOrigin:", !!authorizeRequest.frontendOrigin);
7256
7659
  const providerConfig = this.config.providers[authorizeRequest.provider];
7257
7660
  if (!providerConfig) {
7258
7661
  throw new Error(`Provider ${authorizeRequest.provider} not configured. Add OAuth credentials to your API route configuration.`);
@@ -7293,22 +7696,14 @@ class OAuthHandler {
7293
7696
  const data = await response.json();
7294
7697
  const result = data;
7295
7698
  if (!result.authorizationUrl) {
7296
- console.error("[OAuth] MCP server did not return authorizationUrl:", data);
7297
7699
  throw new Error("MCP server failed to return authorization URL");
7298
7700
  }
7299
- const urlPreview = result.authorizationUrl.length > 100 ? result.authorizationUrl.substring(0, 100) + "..." : result.authorizationUrl;
7300
- console.log("[OAuth] Generated authorization URL:", urlPreview);
7301
7701
  if (authorizeRequest.codeVerifier) {
7302
7702
  try {
7303
7703
  const { storeCodeVerifier: storeCodeVerifier2 } = await Promise.resolve().then(() => (init_server(), exports_server));
7304
7704
  storeCodeVerifier2(authorizeRequest.state, authorizeRequest.codeVerifier, authorizeRequest.provider, authorizeRequest.frontendOrigin);
7305
- console.log("[OAuth] Stored codeVerifier for state:", authorizeRequest.state.substring(0, 20) + "...", "frontendOrigin:", authorizeRequest.frontendOrigin);
7306
- } catch (error) {
7307
- console.warn("[OAuth] Failed to store codeVerifier:", error);
7308
- }
7309
- } else {
7310
- console.log("[OAuth] No codeVerifier provided in authorize request");
7311
- }
7705
+ } catch (error) {}
7706
+ } else {}
7312
7707
  if (webRequest) {
7313
7708
  try {
7314
7709
  const { detectSessionContext: detectSessionContext2 } = await Promise.resolve().then(() => exports_session_detector);
@@ -7325,9 +7720,7 @@ class OAuthHandler {
7325
7720
  const cookieValue = await createContextCookie2(context, authorizeRequest.provider, secret);
7326
7721
  result.setCookie = getSetCookieHeader2(cookieValue);
7327
7722
  }
7328
- } catch (error) {
7329
- console.warn("[OAuth] Failed to capture user context:", error);
7330
- }
7723
+ } catch (error) {}
7331
7724
  }
7332
7725
  return result;
7333
7726
  }
@@ -7361,9 +7754,7 @@ class OAuthHandler {
7361
7754
  context = contextData.context;
7362
7755
  }
7363
7756
  }
7364
- } catch (error) {
7365
- console.warn("[OAuth] Failed to restore user context:", error);
7366
- }
7757
+ } catch (error) {}
7367
7758
  }
7368
7759
  const url = new URL("/oauth/callback", this.serverUrl);
7369
7760
  const response = await fetch(url.toString(), {
@@ -7397,10 +7788,8 @@ class OAuthHandler {
7397
7788
  expiresAt: result.expiresAt,
7398
7789
  scopes: result.scopes
7399
7790
  };
7400
- await this.config.setProviderToken(callbackRequest.provider, tokenData, context);
7401
- } catch (error) {
7402
- console.error("[OAuth] Failed to save provider token:", error);
7403
- }
7791
+ await this.config.setProviderToken(callbackRequest.provider, tokenData, undefined, context);
7792
+ } catch (error) {}
7404
7793
  }
7405
7794
  if (webRequest) {
7406
7795
  const { getClearCookieHeader: getClearCookieHeader2 } = await Promise.resolve().then(() => exports_context_cookie);
@@ -7445,7 +7834,7 @@ class OAuthHandler {
7445
7834
  }
7446
7835
  if (context) {
7447
7836
  try {
7448
- await this.config.removeProviderToken(request.provider, context);
7837
+ await this.config.removeProviderToken(request.provider, undefined, context);
7449
7838
  } catch (error) {
7450
7839
  console.error(`Failed to delete token for ${request.provider} from database via removeProviderToken:`, error);
7451
7840
  }