integrate-sdk 0.8.27 → 0.8.28-dev.1

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/index.js CHANGED
@@ -655,6 +655,277 @@ function sendCallbackToOpener(params) {
655
655
  window.close();
656
656
  }
657
657
 
658
+ // src/oauth/indexeddb-storage.ts
659
+ class IndexedDBStorage {
660
+ db = null;
661
+ initPromise = null;
662
+ async init() {
663
+ if (this.db) {
664
+ return this.db;
665
+ }
666
+ if (this.initPromise) {
667
+ return this.initPromise;
668
+ }
669
+ this.initPromise = new Promise((resolve, reject) => {
670
+ if (typeof window === "undefined" || !window.indexedDB) {
671
+ reject(new Error("IndexedDB is not available in this environment"));
672
+ return;
673
+ }
674
+ const request = window.indexedDB.open(DB_NAME, DB_VERSION);
675
+ request.onerror = () => {
676
+ this.initPromise = null;
677
+ reject(new Error(`Failed to open IndexedDB: ${request.error?.message}`));
678
+ };
679
+ request.onsuccess = () => {
680
+ this.db = request.result;
681
+ this.initPromise = null;
682
+ resolve(this.db);
683
+ };
684
+ request.onupgradeneeded = (event) => {
685
+ const db = event.target.result;
686
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
687
+ const store = db.createObjectStore(STORE_NAME, { keyPath: "accountId" });
688
+ store.createIndex("provider", "provider", { unique: false });
689
+ store.createIndex("email", "email", { unique: false });
690
+ store.createIndex("provider_email", ["provider", "email"], { unique: true });
691
+ }
692
+ };
693
+ });
694
+ return this.initPromise;
695
+ }
696
+ generateAccountId(provider, email) {
697
+ const str = `${provider}:${email}`;
698
+ let hash = 0;
699
+ for (let i = 0;i < str.length; i++) {
700
+ const char = str.charCodeAt(i);
701
+ hash = (hash << 5) - hash + char;
702
+ hash = hash & hash;
703
+ }
704
+ return `${provider}_${Math.abs(hash).toString(36)}`;
705
+ }
706
+ async saveToken(provider, email, tokenData) {
707
+ const db = await this.init();
708
+ const accountId = this.generateAccountId(provider, email);
709
+ const now = Date.now();
710
+ const entry = {
711
+ provider,
712
+ email,
713
+ accountId,
714
+ tokenData,
715
+ createdAt: now,
716
+ updatedAt: now
717
+ };
718
+ return new Promise((resolve, reject) => {
719
+ const transaction = db.transaction([STORE_NAME], "readwrite");
720
+ const store = transaction.objectStore(STORE_NAME);
721
+ const getRequest = store.get(accountId);
722
+ getRequest.onsuccess = () => {
723
+ const existing = getRequest.result;
724
+ if (existing) {
725
+ entry.createdAt = existing.createdAt;
726
+ }
727
+ const putRequest = store.put(entry);
728
+ putRequest.onsuccess = () => resolve();
729
+ putRequest.onerror = () => reject(new Error(`Failed to save token: ${putRequest.error?.message}`));
730
+ };
731
+ getRequest.onerror = () => reject(new Error(`Failed to check existing token: ${getRequest.error?.message}`));
732
+ });
733
+ }
734
+ async getToken(provider, email) {
735
+ const db = await this.init();
736
+ const accountId = this.generateAccountId(provider, email);
737
+ return new Promise((resolve, reject) => {
738
+ const transaction = db.transaction([STORE_NAME], "readonly");
739
+ const store = transaction.objectStore(STORE_NAME);
740
+ const request = store.get(accountId);
741
+ request.onsuccess = () => {
742
+ const entry = request.result;
743
+ resolve(entry?.tokenData);
744
+ };
745
+ request.onerror = () => reject(new Error(`Failed to get token: ${request.error?.message}`));
746
+ });
747
+ }
748
+ async getTokensByProvider(provider) {
749
+ const db = await this.init();
750
+ const tokens = new Map;
751
+ return new Promise((resolve, reject) => {
752
+ const transaction = db.transaction([STORE_NAME], "readonly");
753
+ const store = transaction.objectStore(STORE_NAME);
754
+ const index = store.index("provider");
755
+ const request = index.getAll(provider);
756
+ request.onsuccess = () => {
757
+ const entries = request.result;
758
+ for (const entry of entries) {
759
+ tokens.set(entry.email, entry.tokenData);
760
+ }
761
+ resolve(tokens);
762
+ };
763
+ request.onerror = () => reject(new Error(`Failed to get tokens: ${request.error?.message}`));
764
+ });
765
+ }
766
+ async listAccounts(provider) {
767
+ const db = await this.init();
768
+ return new Promise((resolve, reject) => {
769
+ const transaction = db.transaction([STORE_NAME], "readonly");
770
+ const store = transaction.objectStore(STORE_NAME);
771
+ const index = store.index("provider");
772
+ const request = index.getAll(provider);
773
+ request.onsuccess = () => {
774
+ const entries = request.result;
775
+ const accounts = entries.map((entry) => ({
776
+ email: entry.email,
777
+ accountId: entry.accountId,
778
+ expiresAt: entry.tokenData.expiresAt,
779
+ scopes: entry.tokenData.scopes,
780
+ createdAt: entry.createdAt
781
+ }));
782
+ resolve(accounts);
783
+ };
784
+ request.onerror = () => reject(new Error(`Failed to list accounts: ${request.error?.message}`));
785
+ });
786
+ }
787
+ async deleteToken(provider, email) {
788
+ const db = await this.init();
789
+ const accountId = this.generateAccountId(provider, email);
790
+ return new Promise((resolve, reject) => {
791
+ const transaction = db.transaction([STORE_NAME], "readwrite");
792
+ const store = transaction.objectStore(STORE_NAME);
793
+ const request = store.delete(accountId);
794
+ request.onsuccess = () => resolve();
795
+ request.onerror = () => reject(new Error(`Failed to delete token: ${request.error?.message}`));
796
+ });
797
+ }
798
+ async deleteTokensByProvider(provider) {
799
+ const db = await this.init();
800
+ return new Promise((resolve, reject) => {
801
+ const transaction = db.transaction([STORE_NAME], "readwrite");
802
+ const store = transaction.objectStore(STORE_NAME);
803
+ const index = store.index("provider");
804
+ const request = index.getAll(provider);
805
+ request.onsuccess = () => {
806
+ const entries = request.result;
807
+ const deletePromises = entries.map((entry) => {
808
+ return new Promise((resolveDelete, rejectDelete) => {
809
+ const deleteRequest = store.delete(entry.accountId);
810
+ deleteRequest.onsuccess = () => resolveDelete();
811
+ deleteRequest.onerror = () => rejectDelete(deleteRequest.error);
812
+ });
813
+ });
814
+ Promise.all(deletePromises).then(() => resolve()).catch((error) => reject(error));
815
+ };
816
+ request.onerror = () => reject(new Error(`Failed to delete tokens: ${request.error?.message}`));
817
+ });
818
+ }
819
+ async clearAll() {
820
+ const db = await this.init();
821
+ return new Promise((resolve, reject) => {
822
+ const transaction = db.transaction([STORE_NAME], "readwrite");
823
+ const store = transaction.objectStore(STORE_NAME);
824
+ const request = store.clear();
825
+ request.onsuccess = () => resolve();
826
+ request.onerror = () => reject(new Error(`Failed to clear tokens: ${request.error?.message}`));
827
+ });
828
+ }
829
+ }
830
+ var DB_NAME = "integrate_oauth_tokens", DB_VERSION = 1, STORE_NAME = "tokens";
831
+
832
+ // src/oauth/email-fetcher.ts
833
+ async function fetchUserEmail(provider, tokenData) {
834
+ try {
835
+ switch (provider.toLowerCase()) {
836
+ case "github":
837
+ return await fetchGitHubEmail(tokenData.accessToken);
838
+ case "gmail":
839
+ case "google":
840
+ return await fetchGoogleEmail(tokenData.accessToken);
841
+ case "notion":
842
+ return await fetchNotionEmail(tokenData.accessToken);
843
+ default:
844
+ return tokenData.email;
845
+ }
846
+ } catch (error) {
847
+ console.error(`Failed to fetch email for ${provider}:`, error);
848
+ return;
849
+ }
850
+ }
851
+ async function fetchGitHubEmail(accessToken) {
852
+ try {
853
+ const userResponse = await fetch("https://api.github.com/user", {
854
+ headers: {
855
+ Authorization: `Bearer ${accessToken}`,
856
+ Accept: "application/vnd.github.v3+json"
857
+ }
858
+ });
859
+ if (!userResponse.ok) {
860
+ return;
861
+ }
862
+ const user = await userResponse.json();
863
+ if (user.email) {
864
+ return user.email;
865
+ }
866
+ const emailsResponse = await fetch("https://api.github.com/user/emails", {
867
+ headers: {
868
+ Authorization: `Bearer ${accessToken}`,
869
+ Accept: "application/vnd.github.v3+json"
870
+ }
871
+ });
872
+ if (!emailsResponse.ok) {
873
+ return;
874
+ }
875
+ const emails = await emailsResponse.json();
876
+ const primaryEmail = emails.find((e) => e.primary && e.verified);
877
+ if (primaryEmail) {
878
+ return primaryEmail.email;
879
+ }
880
+ const verifiedEmail = emails.find((e) => e.verified);
881
+ if (verifiedEmail) {
882
+ return verifiedEmail.email;
883
+ }
884
+ if (emails.length > 0 && emails[0]?.email) {
885
+ return emails[0].email;
886
+ }
887
+ return;
888
+ } catch (error) {
889
+ console.error("Failed to fetch GitHub email:", error);
890
+ return;
891
+ }
892
+ }
893
+ async function fetchGoogleEmail(accessToken) {
894
+ try {
895
+ const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
896
+ headers: {
897
+ Authorization: `Bearer ${accessToken}`
898
+ }
899
+ });
900
+ if (!response.ok) {
901
+ return;
902
+ }
903
+ const user = await response.json();
904
+ return user.email;
905
+ } catch (error) {
906
+ console.error("Failed to fetch Google email:", error);
907
+ return;
908
+ }
909
+ }
910
+ async function fetchNotionEmail(accessToken) {
911
+ try {
912
+ const response = await fetch("https://api.notion.com/v1/users/me", {
913
+ headers: {
914
+ Authorization: `Bearer ${accessToken}`,
915
+ "Notion-Version": "2022-06-28"
916
+ }
917
+ });
918
+ if (!response.ok) {
919
+ return;
920
+ }
921
+ const user = await response.json();
922
+ return user.person?.email;
923
+ } catch (error) {
924
+ console.error("Failed to fetch Notion email:", error);
925
+ return;
926
+ }
927
+ }
928
+
658
929
  // src/oauth/manager.ts
659
930
  class OAuthManager {
660
931
  pendingAuths = new Map;
@@ -666,7 +937,8 @@ class OAuthManager {
666
937
  getTokenCallback;
667
938
  setTokenCallback;
668
939
  removeTokenCallback;
669
- skipLocalStorage;
940
+ indexedDBStorage;
941
+ skipLocalStorage = false;
670
942
  constructor(oauthApiBase, flowConfig, apiBaseUrl, tokenCallbacks) {
671
943
  this.oauthApiBase = oauthApiBase;
672
944
  this.apiBaseUrl = apiBaseUrl;
@@ -679,7 +951,7 @@ class OAuthManager {
679
951
  this.getTokenCallback = tokenCallbacks?.getProviderToken;
680
952
  this.setTokenCallback = tokenCallbacks?.setProviderToken;
681
953
  this.removeTokenCallback = tokenCallbacks?.removeProviderToken;
682
- this.skipLocalStorage = !!(tokenCallbacks?.getProviderToken || tokenCallbacks?.setProviderToken);
954
+ this.indexedDBStorage = new IndexedDBStorage;
683
955
  this.cleanupExpiredPendingAuths();
684
956
  }
685
957
  async initiateFlow(provider, config, returnUrl) {
@@ -737,8 +1009,12 @@ class OAuthManager {
737
1009
  expiresAt: tokenData.expiresAt,
738
1010
  scopes: tokenData.scopes
739
1011
  };
1012
+ const email = await fetchUserEmail(provider, tokenDataToStore);
1013
+ if (email) {
1014
+ tokenDataToStore.email = email;
1015
+ }
740
1016
  this.providerTokens.set(provider, tokenDataToStore);
741
- await this.saveProviderToken(provider, tokenDataToStore);
1017
+ await this.saveProviderToken(provider, tokenDataToStore, email);
742
1018
  this.pendingAuths.delete(state);
743
1019
  this.removePendingAuthFromStorage(state);
744
1020
  return { ...tokenDataToStore, provider };
@@ -774,8 +1050,12 @@ class OAuthManager {
774
1050
  expiresAt: response.expiresAt,
775
1051
  scopes: response.scopes
776
1052
  };
1053
+ const email = await fetchUserEmail(pendingAuth.provider, tokenData);
1054
+ if (email) {
1055
+ tokenData.email = email;
1056
+ }
777
1057
  this.providerTokens.set(pendingAuth.provider, tokenData);
778
- await this.saveProviderToken(pendingAuth.provider, tokenData);
1058
+ await this.saveProviderToken(pendingAuth.provider, tokenData, email);
779
1059
  this.pendingAuths.delete(state);
780
1060
  this.removePendingAuthFromStorage(state);
781
1061
  return { ...tokenData, provider: pendingAuth.provider };
@@ -785,8 +1065,8 @@ class OAuthManager {
785
1065
  throw error;
786
1066
  }
787
1067
  }
788
- async checkAuthStatus(provider) {
789
- const tokenData = await this.getProviderToken(provider);
1068
+ async checkAuthStatus(provider, email) {
1069
+ const tokenData = await this.getProviderToken(provider, email);
790
1070
  if (!tokenData) {
791
1071
  return {
792
1072
  authorized: false,
@@ -800,30 +1080,83 @@ class OAuthManager {
800
1080
  expiresAt: tokenData.expiresAt
801
1081
  };
802
1082
  }
1083
+ async disconnectAccount(provider, email, context) {
1084
+ if (this.removeTokenCallback) {
1085
+ try {
1086
+ await this.removeTokenCallback(provider, email, context);
1087
+ } catch (error) {
1088
+ console.error(`Failed to delete token for ${provider} (${email}) from database:`, error);
1089
+ }
1090
+ } else if (this.setTokenCallback) {
1091
+ try {
1092
+ await this.setTokenCallback(provider, null, email, context);
1093
+ } catch (error) {
1094
+ console.error(`Failed to delete token for ${provider} (${email}) from database:`, error);
1095
+ }
1096
+ }
1097
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1098
+ try {
1099
+ await this.indexedDBStorage.deleteToken(provider, email);
1100
+ } catch (error) {
1101
+ console.error(`Failed to delete token from IndexedDB for ${provider} (${email}):`, error);
1102
+ }
1103
+ }
1104
+ this.providerTokens.delete(provider);
1105
+ }
803
1106
  async disconnectProvider(provider, context) {
804
1107
  if (this.removeTokenCallback) {
805
1108
  try {
806
- await this.removeTokenCallback(provider, context);
1109
+ await this.removeTokenCallback(provider, undefined, context);
807
1110
  } catch (error) {
808
- console.error(`Failed to delete token for ${provider} from database via removeProviderToken:`, error);
1111
+ console.error(`Failed to delete tokens for ${provider} from database:`, error);
809
1112
  }
810
1113
  } else if (this.setTokenCallback) {
811
1114
  try {
812
- const tokenData = await this.getProviderToken(provider, context);
813
- if (tokenData) {
814
- await this.setTokenCallback(provider, null, context);
815
- }
1115
+ await this.setTokenCallback(provider, null, undefined, context);
1116
+ } catch (error) {
1117
+ console.error(`Failed to delete tokens for ${provider} from database:`, error);
1118
+ }
1119
+ }
1120
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1121
+ try {
1122
+ await this.indexedDBStorage.deleteTokensByProvider(provider);
816
1123
  } catch (error) {
817
- console.error(`Failed to delete token for ${provider} from database via setProviderToken:`, error);
1124
+ if (typeof window !== "undefined" && window.localStorage) {
1125
+ try {
1126
+ const key = `integrate_token_${provider}`;
1127
+ window.localStorage.removeItem(key);
1128
+ } catch (localStorageError) {}
1129
+ }
1130
+ }
1131
+ }
1132
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1133
+ if (typeof window !== "undefined" && window.localStorage) {
1134
+ try {
1135
+ const key = `integrate_token_${provider}`;
1136
+ window.localStorage.removeItem(key);
1137
+ } catch (localStorageError) {}
818
1138
  }
819
1139
  }
820
1140
  this.providerTokens.delete(provider);
821
- this.clearProviderToken(provider);
822
1141
  }
823
- async getProviderToken(provider, context) {
1142
+ async listAccounts(provider) {
1143
+ if (this.getTokenCallback) {
1144
+ return [];
1145
+ }
1146
+ if (!this.getTokenCallback) {
1147
+ try {
1148
+ return await this.indexedDBStorage.listAccounts(provider);
1149
+ } catch (error) {
1150
+ console.error(`Failed to list accounts for ${provider}:`, error);
1151
+ return [];
1152
+ }
1153
+ }
1154
+ return [];
1155
+ }
1156
+ async getProviderToken(provider, email, context) {
824
1157
  if (this.getTokenCallback) {
825
1158
  try {
826
- const tokenData = await this.getTokenCallback(provider, context);
1159
+ const tokenData = await this.getTokenCallback(provider, email, context);
827
1160
  if (tokenData) {
828
1161
  this.providerTokens.set(provider, tokenData);
829
1162
  }
@@ -833,6 +1166,17 @@ class OAuthManager {
833
1166
  return;
834
1167
  }
835
1168
  }
1169
+ if (email && !this.getTokenCallback) {
1170
+ try {
1171
+ const tokenData = await this.indexedDBStorage.getToken(provider, email);
1172
+ if (tokenData) {
1173
+ this.providerTokens.set(provider, tokenData);
1174
+ }
1175
+ return tokenData;
1176
+ } catch (error) {
1177
+ console.error(`Failed to get token from IndexedDB for ${provider}:`, error);
1178
+ }
1179
+ }
836
1180
  return this.providerTokens.get(provider);
837
1181
  }
838
1182
  getAllProviderTokens() {
@@ -841,35 +1185,45 @@ class OAuthManager {
841
1185
  getProviderTokenFromCache(provider) {
842
1186
  return this.providerTokens.get(provider);
843
1187
  }
844
- async setProviderToken(provider, tokenData, context) {
1188
+ async setProviderToken(provider, tokenData, email, context) {
1189
+ const tokenEmail = email || tokenData?.email;
845
1190
  if (tokenData === null) {
846
- this.providerTokens.delete(provider);
1191
+ if (tokenEmail) {
1192
+ this.providerTokens.delete(provider);
1193
+ } else {
1194
+ this.providerTokens.delete(provider);
1195
+ }
847
1196
  } else {
848
1197
  this.providerTokens.set(provider, tokenData);
849
1198
  }
850
- await this.saveProviderToken(provider, tokenData, context);
1199
+ await this.saveProviderToken(provider, tokenData, tokenEmail, context);
851
1200
  }
852
1201
  clearProviderToken(provider) {
853
1202
  this.providerTokens.delete(provider);
854
- if (!this.skipLocalStorage && typeof window !== "undefined" && window.localStorage) {
855
- try {
856
- window.localStorage.removeItem(`integrate_token_${provider}`);
857
- } catch (error) {
858
- console.error(`Failed to clear token for ${provider} from localStorage:`, error);
859
- }
1203
+ if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
1204
+ this.indexedDBStorage.deleteTokensByProvider(provider).catch((error) => {
1205
+ console.error(`Failed to clear tokens for ${provider} from IndexedDB:`, error);
1206
+ });
860
1207
  }
861
1208
  }
862
1209
  clearAllProviderTokens() {
863
- const providers = Array.from(this.providerTokens.keys());
864
1210
  this.providerTokens.clear();
865
- if (!this.skipLocalStorage && typeof window !== "undefined" && window.localStorage) {
866
- for (const provider of providers) {
1211
+ if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
1212
+ if (typeof window !== "undefined" && window.localStorage) {
867
1213
  try {
868
- window.localStorage.removeItem(`integrate_token_${provider}`);
869
- } catch (error) {
870
- console.error(`Failed to clear token for ${provider} from localStorage:`, error);
871
- }
1214
+ const keysToRemove = [];
1215
+ for (let i = 0;i < window.localStorage.length; i++) {
1216
+ const key = window.localStorage.key(i);
1217
+ if (key && key.startsWith("integrate_token_")) {
1218
+ keysToRemove.push(key);
1219
+ }
1220
+ }
1221
+ keysToRemove.forEach((key) => window.localStorage.removeItem(key));
1222
+ } catch (localStorageError) {}
872
1223
  }
1224
+ this.indexedDBStorage.clearAll().catch((error) => {
1225
+ console.error("Failed to clear all tokens from IndexedDB:", error);
1226
+ });
873
1227
  }
874
1228
  }
875
1229
  clearAllPendingAuths() {
@@ -890,66 +1244,125 @@ class OAuthManager {
890
1244
  }
891
1245
  }
892
1246
  }
893
- async saveProviderToken(provider, tokenData, context) {
1247
+ async saveProviderToken(provider, tokenData, email, context) {
894
1248
  if (this.setTokenCallback) {
895
1249
  try {
896
- await this.setTokenCallback(provider, tokenData, context);
1250
+ await this.setTokenCallback(provider, tokenData, email, context);
897
1251
  } catch (error) {
898
1252
  console.error(`Failed to ${tokenData === null ? "delete" : "save"} token for ${provider} via callback:`, error);
899
1253
  throw error;
900
1254
  }
901
1255
  return;
902
1256
  }
903
- if (tokenData === null) {
904
- this.clearProviderToken(provider);
1257
+ if (this.skipLocalStorage) {
905
1258
  return;
906
1259
  }
907
- if (this.skipLocalStorage) {
1260
+ if (tokenData === null) {
1261
+ if (email) {
1262
+ if (!this.setTokenCallback && !this.removeTokenCallback) {
1263
+ await this.indexedDBStorage.deleteToken(provider, email).catch(() => {
1264
+ if (typeof window !== "undefined" && window.localStorage) {
1265
+ try {
1266
+ const key = `integrate_token_${provider}`;
1267
+ window.localStorage.removeItem(key);
1268
+ } catch (localStorageError) {}
1269
+ }
1270
+ });
1271
+ }
1272
+ } else {
1273
+ this.clearProviderToken(provider);
1274
+ if (typeof window !== "undefined" && window.localStorage) {
1275
+ try {
1276
+ const key = `integrate_token_${provider}`;
1277
+ window.localStorage.removeItem(key);
1278
+ } catch (localStorageError) {}
1279
+ }
1280
+ }
908
1281
  return;
909
1282
  }
910
- if (typeof window !== "undefined" && window.localStorage) {
1283
+ const tokenEmail = email || tokenData.email;
1284
+ if (tokenEmail) {
911
1285
  try {
912
- const key = `integrate_token_${provider}`;
913
- window.localStorage.setItem(key, JSON.stringify(tokenData));
1286
+ await this.indexedDBStorage.saveToken(provider, tokenEmail, tokenData);
914
1287
  } catch (error) {
915
- console.error(`Failed to save token for ${provider} to localStorage:`, error);
1288
+ if (typeof window !== "undefined" && window.localStorage) {
1289
+ try {
1290
+ const key = `integrate_token_${provider}`;
1291
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1292
+ } catch (localStorageError) {
1293
+ console.error(`Failed to save token for ${provider} to localStorage:`, localStorageError);
1294
+ }
1295
+ } else {
1296
+ console.error(`Failed to save token for ${provider} to IndexedDB:`, error);
1297
+ }
1298
+ }
1299
+ } else {
1300
+ if (typeof window !== "undefined" && window.localStorage) {
1301
+ try {
1302
+ const key = `integrate_token_${provider}`;
1303
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1304
+ } catch (localStorageError) {
1305
+ console.error(`Failed to save token for ${provider} to localStorage:`, localStorageError);
1306
+ }
916
1307
  }
917
1308
  }
918
1309
  }
919
- async loadProviderToken(provider) {
1310
+ async loadProviderToken(provider, email, context) {
920
1311
  if (this.getTokenCallback) {
921
1312
  try {
922
- return await this.getTokenCallback(provider);
1313
+ return await this.getTokenCallback(provider, email, context);
923
1314
  } catch (error) {
924
1315
  console.error(`Failed to load token for ${provider} via callback:`, error);
925
1316
  return;
926
1317
  }
927
1318
  }
928
1319
  if (this.skipLocalStorage) {
929
- return;
1320
+ return this.providerTokens.get(provider);
930
1321
  }
931
- if (typeof window !== "undefined" && window.localStorage) {
1322
+ if (email) {
932
1323
  try {
933
- const key = `integrate_token_${provider}`;
934
- const stored = window.localStorage.getItem(key);
935
- if (stored) {
936
- return JSON.parse(stored);
1324
+ return await this.indexedDBStorage.getToken(provider, email);
1325
+ } catch (error) {
1326
+ if (typeof window !== "undefined" && window.localStorage) {
1327
+ try {
1328
+ const key = `integrate_token_${provider}`;
1329
+ const stored = window.localStorage.getItem(key);
1330
+ if (stored) {
1331
+ return JSON.parse(stored);
1332
+ }
1333
+ } catch (localStorageError) {}
1334
+ }
1335
+ return;
1336
+ }
1337
+ } else {
1338
+ try {
1339
+ const tokens = await this.indexedDBStorage.getTokensByProvider(provider);
1340
+ if (tokens.size > 0) {
1341
+ return tokens.values().next().value;
937
1342
  }
938
1343
  } catch (error) {
939
- console.error(`Failed to load token for ${provider} from localStorage:`, error);
1344
+ if (typeof window !== "undefined" && window.localStorage) {
1345
+ try {
1346
+ const key = `integrate_token_${provider}`;
1347
+ const stored = window.localStorage.getItem(key);
1348
+ if (stored) {
1349
+ return JSON.parse(stored);
1350
+ }
1351
+ } catch (localStorageError) {}
1352
+ }
940
1353
  }
941
1354
  }
942
1355
  return;
943
1356
  }
944
1357
  async loadAllProviderTokens(providers) {
945
1358
  for (const provider of providers) {
946
- const tokenData = await this.loadProviderToken(provider);
1359
+ const tokenData = await this.loadProviderToken(provider, undefined, undefined);
947
1360
  if (tokenData) {
948
1361
  this.providerTokens.set(provider, tokenData);
949
1362
  }
950
1363
  }
951
1364
  }
952
- loadProviderTokenSync(provider) {
1365
+ loadAllProviderTokensSync(providers) {
953
1366
  if (this.getTokenCallback) {
954
1367
  return;
955
1368
  }
@@ -957,28 +1370,18 @@ class OAuthManager {
957
1370
  return;
958
1371
  }
959
1372
  if (typeof window !== "undefined" && window.localStorage) {
960
- try {
961
- const key = `integrate_token_${provider}`;
962
- const stored = window.localStorage.getItem(key);
963
- if (stored) {
964
- return JSON.parse(stored);
965
- }
966
- } catch (error) {
967
- console.error(`Failed to load token for ${provider} from localStorage:`, error);
968
- }
969
- }
970
- return;
971
- }
972
- loadAllProviderTokensSync(providers) {
973
- if (this.getTokenCallback) {
974
- return;
975
- }
976
- for (const provider of providers) {
977
- const tokenData = this.loadProviderTokenSync(provider);
978
- if (tokenData) {
979
- this.providerTokens.set(provider, tokenData);
1373
+ for (const provider of providers) {
1374
+ try {
1375
+ const key = `integrate_token_${provider}`;
1376
+ const stored = window.localStorage.getItem(key);
1377
+ if (stored) {
1378
+ const tokenData = JSON.parse(stored);
1379
+ this.providerTokens.set(provider, tokenData);
1380
+ }
1381
+ } catch {}
980
1382
  }
981
1383
  }
1384
+ this.loadAllProviderTokens(providers).catch(() => {});
982
1385
  }
983
1386
  savePendingAuthToStorage(state, pendingAuth) {
984
1387
  if (typeof window !== "undefined" && window.localStorage) {
@@ -1061,7 +1464,7 @@ class OAuthManager {
1061
1464
  })
1062
1465
  });
1063
1466
  if (response.headers.get("X-Integrate-Use-Database") === "true") {
1064
- this.skipLocalStorage = true;
1467
+ this.setSkipLocalStorage(true);
1065
1468
  }
1066
1469
  if (!response.ok) {
1067
1470
  const error = await response.text();
@@ -1091,7 +1494,7 @@ class OAuthManager {
1091
1494
  })
1092
1495
  });
1093
1496
  if (response.headers.get("X-Integrate-Use-Database") === "true") {
1094
- this.skipLocalStorage = true;
1497
+ this.setSkipLocalStorage(true);
1095
1498
  }
1096
1499
  if (!response.ok) {
1097
1500
  const error = await response.text();
@@ -1360,7 +1763,7 @@ class MCPClientBase {
1360
1763
  const hasApiKey = !!transportHeaders["X-API-KEY"];
1361
1764
  if (hasApiKey) {
1362
1765
  if (provider) {
1363
- const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
1766
+ const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
1364
1767
  if (tokenData && this.transport.setHeader) {
1365
1768
  const previousAuthHeader = transportHeaders["Authorization"];
1366
1769
  try {
@@ -1390,7 +1793,7 @@ class MCPClientBase {
1390
1793
  "Content-Type": "application/json"
1391
1794
  };
1392
1795
  if (provider) {
1393
- const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
1796
+ const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
1394
1797
  if (tokenData) {
1395
1798
  headers["Authorization"] = `Bearer ${tokenData.accessToken}`;
1396
1799
  }
@@ -1534,6 +1937,29 @@ class MCPClientBase {
1534
1937
  throw error;
1535
1938
  }
1536
1939
  }
1940
+ async disconnectAccount(provider, email, context) {
1941
+ const integration = this.integrations.find((p) => p.oauth?.provider === provider);
1942
+ if (!integration?.oauth) {
1943
+ throw new Error(`No OAuth configuration found for provider: ${provider}`);
1944
+ }
1945
+ try {
1946
+ await this.oauthManager.disconnectAccount(provider, email, context);
1947
+ const accounts = await this.oauthManager.listAccounts(provider);
1948
+ if (accounts.length === 0) {
1949
+ this.authState.set(provider, { authenticated: false });
1950
+ }
1951
+ this.eventEmitter.emit("auth:disconnect", { provider });
1952
+ } catch (error) {
1953
+ this.eventEmitter.emit("auth:error", {
1954
+ provider,
1955
+ error
1956
+ });
1957
+ throw error;
1958
+ }
1959
+ }
1960
+ async listAccounts(provider) {
1961
+ return await this.oauthManager.listAccounts(provider);
1962
+ }
1537
1963
  async logout() {
1538
1964
  this.clearSessionToken();
1539
1965
  this.oauthManager.clearAllPendingAuths();
@@ -1566,13 +1992,13 @@ class MCPClientBase {
1566
1992
  isProviderAuthenticated(provider) {
1567
1993
  return this.authState.get(provider)?.authenticated ?? false;
1568
1994
  }
1569
- async isAuthorized(provider, context) {
1995
+ async isAuthorized(provider, email, context) {
1570
1996
  if (this.oauthCallbackPromise) {
1571
1997
  await this.oauthCallbackPromise;
1572
1998
  this.oauthCallbackPromise = null;
1573
1999
  }
1574
2000
  try {
1575
- const tokenData = await this.oauthManager.getProviderToken(provider, context);
2001
+ const tokenData = await this.oauthManager.getProviderToken(provider, email, context);
1576
2002
  const isAuthenticated = !!tokenData;
1577
2003
  const currentState = this.authState.get(provider);
1578
2004
  if (currentState) {
@@ -1610,8 +2036,8 @@ class MCPClientBase {
1610
2036
  }
1611
2037
  return authorized;
1612
2038
  }
1613
- async getAuthorizationStatus(provider) {
1614
- return await this.oauthManager.checkAuthStatus(provider);
2039
+ async getAuthorizationStatus(provider, email) {
2040
+ return await this.oauthManager.checkAuthStatus(provider, email);
1615
2041
  }
1616
2042
  async authorize(provider, options) {
1617
2043
  const integration = this.integrations.find((p) => p.oauth?.provider === provider);
@@ -1669,11 +2095,11 @@ class MCPClientBase {
1669
2095
  throw error;
1670
2096
  }
1671
2097
  }
1672
- async getProviderToken(provider, context) {
1673
- return await this.oauthManager.getProviderToken(provider, context);
2098
+ async getProviderToken(provider, email, context) {
2099
+ return await this.oauthManager.getProviderToken(provider, email, context);
1674
2100
  }
1675
- async setProviderToken(provider, tokenData, context) {
1676
- await this.oauthManager.setProviderToken(provider, tokenData, context);
2101
+ async setProviderToken(provider, tokenData, email, context) {
2102
+ await this.oauthManager.setProviderToken(provider, tokenData, email, context);
1677
2103
  if (tokenData === null) {
1678
2104
  this.authState.set(provider, { authenticated: false });
1679
2105
  } else {
@@ -7742,7 +8168,7 @@ class OAuthHandler {
7742
8168
  expiresAt: result.expiresAt,
7743
8169
  scopes: result.scopes
7744
8170
  };
7745
- await this.config.setProviderToken(callbackRequest.provider, tokenData, context);
8171
+ await this.config.setProviderToken(callbackRequest.provider, tokenData, undefined, context);
7746
8172
  } catch (error) {}
7747
8173
  }
7748
8174
  if (webRequest) {
@@ -7788,7 +8214,7 @@ class OAuthHandler {
7788
8214
  }
7789
8215
  if (context) {
7790
8216
  try {
7791
- await this.config.removeProviderToken(request.provider, context);
8217
+ await this.config.removeProviderToken(request.provider, undefined, context);
7792
8218
  } catch (error) {
7793
8219
  console.error(`Failed to delete token for ${request.provider} from database via removeProviderToken:`, error);
7794
8220
  }