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/adapters/auto-routes.js +512 -86
- package/dist/adapters/base-handler.d.ts +19 -11
- package/dist/adapters/base-handler.d.ts.map +1 -1
- package/dist/adapters/index.js +512 -86
- package/dist/adapters/nextjs.js +512 -86
- package/dist/adapters/node.js +512 -86
- package/dist/adapters/svelte-kit.js +512 -86
- package/dist/adapters/tanstack-start.js +512 -86
- package/dist/index.js +512 -86
- package/dist/oauth.js +512 -86
- package/dist/server.js +512 -86
- package/dist/src/adapters/base-handler.d.ts +19 -11
- package/dist/src/adapters/base-handler.d.ts.map +1 -1
- package/dist/src/client.d.ts +68 -12
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/config/types.d.ts +39 -23
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/oauth/email-fetcher.d.ts +14 -0
- package/dist/src/oauth/email-fetcher.d.ts.map +1 -0
- package/dist/src/oauth/indexeddb-storage.d.ts +68 -0
- package/dist/src/oauth/indexeddb-storage.d.ts.map +1 -0
- package/dist/src/oauth/manager.d.ts +59 -41
- package/dist/src/oauth/manager.d.ts.map +1 -1
- package/dist/src/oauth/types.d.ts +19 -0
- package/dist/src/oauth/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/adapters/nextjs.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
926
|
+
this.indexedDBStorage = new IndexedDBStorage;
|
|
655
927
|
this.cleanupExpiredPendingAuths();
|
|
656
928
|
}
|
|
657
929
|
async initiateFlow(provider, config, returnUrl) {
|
|
@@ -709,8 +981,12 @@ class OAuthManager {
|
|
|
709
981
|
expiresAt: tokenData.expiresAt,
|
|
710
982
|
scopes: tokenData.scopes
|
|
711
983
|
};
|
|
984
|
+
const email = await fetchUserEmail(provider, tokenDataToStore);
|
|
985
|
+
if (email) {
|
|
986
|
+
tokenDataToStore.email = email;
|
|
987
|
+
}
|
|
712
988
|
this.providerTokens.set(provider, tokenDataToStore);
|
|
713
|
-
await this.saveProviderToken(provider, tokenDataToStore);
|
|
989
|
+
await this.saveProviderToken(provider, tokenDataToStore, email);
|
|
714
990
|
this.pendingAuths.delete(state);
|
|
715
991
|
this.removePendingAuthFromStorage(state);
|
|
716
992
|
return { ...tokenDataToStore, provider };
|
|
@@ -746,8 +1022,12 @@ class OAuthManager {
|
|
|
746
1022
|
expiresAt: response.expiresAt,
|
|
747
1023
|
scopes: response.scopes
|
|
748
1024
|
};
|
|
1025
|
+
const email = await fetchUserEmail(pendingAuth.provider, tokenData);
|
|
1026
|
+
if (email) {
|
|
1027
|
+
tokenData.email = email;
|
|
1028
|
+
}
|
|
749
1029
|
this.providerTokens.set(pendingAuth.provider, tokenData);
|
|
750
|
-
await this.saveProviderToken(pendingAuth.provider, tokenData);
|
|
1030
|
+
await this.saveProviderToken(pendingAuth.provider, tokenData, email);
|
|
751
1031
|
this.pendingAuths.delete(state);
|
|
752
1032
|
this.removePendingAuthFromStorage(state);
|
|
753
1033
|
return { ...tokenData, provider: pendingAuth.provider };
|
|
@@ -757,8 +1037,8 @@ class OAuthManager {
|
|
|
757
1037
|
throw error;
|
|
758
1038
|
}
|
|
759
1039
|
}
|
|
760
|
-
async checkAuthStatus(provider) {
|
|
761
|
-
const tokenData = await this.getProviderToken(provider);
|
|
1040
|
+
async checkAuthStatus(provider, email) {
|
|
1041
|
+
const tokenData = await this.getProviderToken(provider, email);
|
|
762
1042
|
if (!tokenData) {
|
|
763
1043
|
return {
|
|
764
1044
|
authorized: false,
|
|
@@ -772,30 +1052,83 @@ class OAuthManager {
|
|
|
772
1052
|
expiresAt: tokenData.expiresAt
|
|
773
1053
|
};
|
|
774
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
|
+
}
|
|
775
1078
|
async disconnectProvider(provider, context) {
|
|
776
1079
|
if (this.removeTokenCallback) {
|
|
777
1080
|
try {
|
|
778
|
-
await this.removeTokenCallback(provider, context);
|
|
1081
|
+
await this.removeTokenCallback(provider, undefined, context);
|
|
779
1082
|
} catch (error) {
|
|
780
|
-
console.error(`Failed to delete
|
|
1083
|
+
console.error(`Failed to delete tokens for ${provider} from database:`, error);
|
|
781
1084
|
}
|
|
782
1085
|
} else if (this.setTokenCallback) {
|
|
783
1086
|
try {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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);
|
|
788
1095
|
} catch (error) {
|
|
789
|
-
|
|
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) {}
|
|
790
1110
|
}
|
|
791
1111
|
}
|
|
792
1112
|
this.providerTokens.delete(provider);
|
|
793
|
-
this.clearProviderToken(provider);
|
|
794
1113
|
}
|
|
795
|
-
async
|
|
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) {
|
|
796
1129
|
if (this.getTokenCallback) {
|
|
797
1130
|
try {
|
|
798
|
-
const tokenData = await this.getTokenCallback(provider, context);
|
|
1131
|
+
const tokenData = await this.getTokenCallback(provider, email, context);
|
|
799
1132
|
if (tokenData) {
|
|
800
1133
|
this.providerTokens.set(provider, tokenData);
|
|
801
1134
|
}
|
|
@@ -805,6 +1138,17 @@ class OAuthManager {
|
|
|
805
1138
|
return;
|
|
806
1139
|
}
|
|
807
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
|
+
}
|
|
808
1152
|
return this.providerTokens.get(provider);
|
|
809
1153
|
}
|
|
810
1154
|
getAllProviderTokens() {
|
|
@@ -813,35 +1157,45 @@ class OAuthManager {
|
|
|
813
1157
|
getProviderTokenFromCache(provider) {
|
|
814
1158
|
return this.providerTokens.get(provider);
|
|
815
1159
|
}
|
|
816
|
-
async setProviderToken(provider, tokenData, context) {
|
|
1160
|
+
async setProviderToken(provider, tokenData, email, context) {
|
|
1161
|
+
const tokenEmail = email || tokenData?.email;
|
|
817
1162
|
if (tokenData === null) {
|
|
818
|
-
|
|
1163
|
+
if (tokenEmail) {
|
|
1164
|
+
this.providerTokens.delete(provider);
|
|
1165
|
+
} else {
|
|
1166
|
+
this.providerTokens.delete(provider);
|
|
1167
|
+
}
|
|
819
1168
|
} else {
|
|
820
1169
|
this.providerTokens.set(provider, tokenData);
|
|
821
1170
|
}
|
|
822
|
-
await this.saveProviderToken(provider, tokenData, context);
|
|
1171
|
+
await this.saveProviderToken(provider, tokenData, tokenEmail, context);
|
|
823
1172
|
}
|
|
824
1173
|
clearProviderToken(provider) {
|
|
825
1174
|
this.providerTokens.delete(provider);
|
|
826
|
-
if (!this.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
console.error(`Failed to clear token for ${provider} from localStorage:`, error);
|
|
831
|
-
}
|
|
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
|
+
});
|
|
832
1179
|
}
|
|
833
1180
|
}
|
|
834
1181
|
clearAllProviderTokens() {
|
|
835
|
-
const providers = Array.from(this.providerTokens.keys());
|
|
836
1182
|
this.providerTokens.clear();
|
|
837
|
-
if (!this.
|
|
838
|
-
|
|
1183
|
+
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
1184
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
839
1185
|
try {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
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) {}
|
|
844
1195
|
}
|
|
1196
|
+
this.indexedDBStorage.clearAll().catch((error) => {
|
|
1197
|
+
console.error("Failed to clear all tokens from IndexedDB:", error);
|
|
1198
|
+
});
|
|
845
1199
|
}
|
|
846
1200
|
}
|
|
847
1201
|
clearAllPendingAuths() {
|
|
@@ -862,66 +1216,125 @@ class OAuthManager {
|
|
|
862
1216
|
}
|
|
863
1217
|
}
|
|
864
1218
|
}
|
|
865
|
-
async saveProviderToken(provider, tokenData, context) {
|
|
1219
|
+
async saveProviderToken(provider, tokenData, email, context) {
|
|
866
1220
|
if (this.setTokenCallback) {
|
|
867
1221
|
try {
|
|
868
|
-
await this.setTokenCallback(provider, tokenData, context);
|
|
1222
|
+
await this.setTokenCallback(provider, tokenData, email, context);
|
|
869
1223
|
} catch (error) {
|
|
870
1224
|
console.error(`Failed to ${tokenData === null ? "delete" : "save"} token for ${provider} via callback:`, error);
|
|
871
1225
|
throw error;
|
|
872
1226
|
}
|
|
873
1227
|
return;
|
|
874
1228
|
}
|
|
875
|
-
if (
|
|
876
|
-
this.clearProviderToken(provider);
|
|
1229
|
+
if (this.skipLocalStorage) {
|
|
877
1230
|
return;
|
|
878
1231
|
}
|
|
879
|
-
if (
|
|
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
|
+
}
|
|
880
1253
|
return;
|
|
881
1254
|
}
|
|
882
|
-
|
|
1255
|
+
const tokenEmail = email || tokenData.email;
|
|
1256
|
+
if (tokenEmail) {
|
|
883
1257
|
try {
|
|
884
|
-
|
|
885
|
-
window.localStorage.setItem(key, JSON.stringify(tokenData));
|
|
1258
|
+
await this.indexedDBStorage.saveToken(provider, tokenEmail, tokenData);
|
|
886
1259
|
} catch (error) {
|
|
887
|
-
|
|
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
|
+
}
|
|
888
1279
|
}
|
|
889
1280
|
}
|
|
890
1281
|
}
|
|
891
|
-
async loadProviderToken(provider) {
|
|
1282
|
+
async loadProviderToken(provider, email, context) {
|
|
892
1283
|
if (this.getTokenCallback) {
|
|
893
1284
|
try {
|
|
894
|
-
return await this.getTokenCallback(provider);
|
|
1285
|
+
return await this.getTokenCallback(provider, email, context);
|
|
895
1286
|
} catch (error) {
|
|
896
1287
|
console.error(`Failed to load token for ${provider} via callback:`, error);
|
|
897
1288
|
return;
|
|
898
1289
|
}
|
|
899
1290
|
}
|
|
900
1291
|
if (this.skipLocalStorage) {
|
|
901
|
-
return;
|
|
1292
|
+
return this.providerTokens.get(provider);
|
|
902
1293
|
}
|
|
903
|
-
if (
|
|
1294
|
+
if (email) {
|
|
904
1295
|
try {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
if (
|
|
908
|
-
|
|
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;
|
|
909
1314
|
}
|
|
910
1315
|
} catch (error) {
|
|
911
|
-
|
|
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
|
+
}
|
|
912
1325
|
}
|
|
913
1326
|
}
|
|
914
1327
|
return;
|
|
915
1328
|
}
|
|
916
1329
|
async loadAllProviderTokens(providers) {
|
|
917
1330
|
for (const provider of providers) {
|
|
918
|
-
const tokenData = await this.loadProviderToken(provider);
|
|
1331
|
+
const tokenData = await this.loadProviderToken(provider, undefined, undefined);
|
|
919
1332
|
if (tokenData) {
|
|
920
1333
|
this.providerTokens.set(provider, tokenData);
|
|
921
1334
|
}
|
|
922
1335
|
}
|
|
923
1336
|
}
|
|
924
|
-
|
|
1337
|
+
loadAllProviderTokensSync(providers) {
|
|
925
1338
|
if (this.getTokenCallback) {
|
|
926
1339
|
return;
|
|
927
1340
|
}
|
|
@@ -929,28 +1342,18 @@ class OAuthManager {
|
|
|
929
1342
|
return;
|
|
930
1343
|
}
|
|
931
1344
|
if (typeof window !== "undefined" && window.localStorage) {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
}
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
loadAllProviderTokensSync(providers) {
|
|
945
|
-
if (this.getTokenCallback) {
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
for (const provider of providers) {
|
|
949
|
-
const tokenData = this.loadProviderTokenSync(provider);
|
|
950
|
-
if (tokenData) {
|
|
951
|
-
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 {}
|
|
952
1354
|
}
|
|
953
1355
|
}
|
|
1356
|
+
this.loadAllProviderTokens(providers).catch(() => {});
|
|
954
1357
|
}
|
|
955
1358
|
savePendingAuthToStorage(state, pendingAuth) {
|
|
956
1359
|
if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -1033,7 +1436,7 @@ class OAuthManager {
|
|
|
1033
1436
|
})
|
|
1034
1437
|
});
|
|
1035
1438
|
if (response.headers.get("X-Integrate-Use-Database") === "true") {
|
|
1036
|
-
this.
|
|
1439
|
+
this.setSkipLocalStorage(true);
|
|
1037
1440
|
}
|
|
1038
1441
|
if (!response.ok) {
|
|
1039
1442
|
const error = await response.text();
|
|
@@ -1063,7 +1466,7 @@ class OAuthManager {
|
|
|
1063
1466
|
})
|
|
1064
1467
|
});
|
|
1065
1468
|
if (response.headers.get("X-Integrate-Use-Database") === "true") {
|
|
1066
|
-
this.
|
|
1469
|
+
this.setSkipLocalStorage(true);
|
|
1067
1470
|
}
|
|
1068
1471
|
if (!response.ok) {
|
|
1069
1472
|
const error = await response.text();
|
|
@@ -1332,7 +1735,7 @@ class MCPClientBase {
|
|
|
1332
1735
|
const hasApiKey = !!transportHeaders["X-API-KEY"];
|
|
1333
1736
|
if (hasApiKey) {
|
|
1334
1737
|
if (provider) {
|
|
1335
|
-
const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
|
|
1738
|
+
const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
|
|
1336
1739
|
if (tokenData && this.transport.setHeader) {
|
|
1337
1740
|
const previousAuthHeader = transportHeaders["Authorization"];
|
|
1338
1741
|
try {
|
|
@@ -1362,7 +1765,7 @@ class MCPClientBase {
|
|
|
1362
1765
|
"Content-Type": "application/json"
|
|
1363
1766
|
};
|
|
1364
1767
|
if (provider) {
|
|
1365
|
-
const tokenData = await this.oauthManager.getProviderToken(provider, options?.context);
|
|
1768
|
+
const tokenData = await this.oauthManager.getProviderToken(provider, undefined, options?.context);
|
|
1366
1769
|
if (tokenData) {
|
|
1367
1770
|
headers["Authorization"] = `Bearer ${tokenData.accessToken}`;
|
|
1368
1771
|
}
|
|
@@ -1506,6 +1909,29 @@ class MCPClientBase {
|
|
|
1506
1909
|
throw error;
|
|
1507
1910
|
}
|
|
1508
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
|
+
}
|
|
1509
1935
|
async logout() {
|
|
1510
1936
|
this.clearSessionToken();
|
|
1511
1937
|
this.oauthManager.clearAllPendingAuths();
|
|
@@ -1538,13 +1964,13 @@ class MCPClientBase {
|
|
|
1538
1964
|
isProviderAuthenticated(provider) {
|
|
1539
1965
|
return this.authState.get(provider)?.authenticated ?? false;
|
|
1540
1966
|
}
|
|
1541
|
-
async isAuthorized(provider, context) {
|
|
1967
|
+
async isAuthorized(provider, email, context) {
|
|
1542
1968
|
if (this.oauthCallbackPromise) {
|
|
1543
1969
|
await this.oauthCallbackPromise;
|
|
1544
1970
|
this.oauthCallbackPromise = null;
|
|
1545
1971
|
}
|
|
1546
1972
|
try {
|
|
1547
|
-
const tokenData = await this.oauthManager.getProviderToken(provider, context);
|
|
1973
|
+
const tokenData = await this.oauthManager.getProviderToken(provider, email, context);
|
|
1548
1974
|
const isAuthenticated = !!tokenData;
|
|
1549
1975
|
const currentState = this.authState.get(provider);
|
|
1550
1976
|
if (currentState) {
|
|
@@ -1582,8 +2008,8 @@ class MCPClientBase {
|
|
|
1582
2008
|
}
|
|
1583
2009
|
return authorized;
|
|
1584
2010
|
}
|
|
1585
|
-
async getAuthorizationStatus(provider) {
|
|
1586
|
-
return await this.oauthManager.checkAuthStatus(provider);
|
|
2011
|
+
async getAuthorizationStatus(provider, email) {
|
|
2012
|
+
return await this.oauthManager.checkAuthStatus(provider, email);
|
|
1587
2013
|
}
|
|
1588
2014
|
async authorize(provider, options) {
|
|
1589
2015
|
const integration = this.integrations.find((p) => p.oauth?.provider === provider);
|
|
@@ -1641,11 +2067,11 @@ class MCPClientBase {
|
|
|
1641
2067
|
throw error;
|
|
1642
2068
|
}
|
|
1643
2069
|
}
|
|
1644
|
-
async getProviderToken(provider, context) {
|
|
1645
|
-
return await this.oauthManager.getProviderToken(provider, context);
|
|
2070
|
+
async getProviderToken(provider, email, context) {
|
|
2071
|
+
return await this.oauthManager.getProviderToken(provider, email, context);
|
|
1646
2072
|
}
|
|
1647
|
-
async setProviderToken(provider, tokenData, context) {
|
|
1648
|
-
await this.oauthManager.setProviderToken(provider, tokenData, context);
|
|
2073
|
+
async setProviderToken(provider, tokenData, email, context) {
|
|
2074
|
+
await this.oauthManager.setProviderToken(provider, tokenData, email, context);
|
|
1649
2075
|
if (tokenData === null) {
|
|
1650
2076
|
this.authState.set(provider, { authenticated: false });
|
|
1651
2077
|
} else {
|
|
@@ -7362,7 +7788,7 @@ class OAuthHandler {
|
|
|
7362
7788
|
expiresAt: result.expiresAt,
|
|
7363
7789
|
scopes: result.scopes
|
|
7364
7790
|
};
|
|
7365
|
-
await this.config.setProviderToken(callbackRequest.provider, tokenData, context);
|
|
7791
|
+
await this.config.setProviderToken(callbackRequest.provider, tokenData, undefined, context);
|
|
7366
7792
|
} catch (error) {}
|
|
7367
7793
|
}
|
|
7368
7794
|
if (webRequest) {
|
|
@@ -7408,7 +7834,7 @@ class OAuthHandler {
|
|
|
7408
7834
|
}
|
|
7409
7835
|
if (context) {
|
|
7410
7836
|
try {
|
|
7411
|
-
await this.config.removeProviderToken(request.provider, context);
|
|
7837
|
+
await this.config.removeProviderToken(request.provider, undefined, context);
|
|
7412
7838
|
} catch (error) {
|
|
7413
7839
|
console.error(`Failed to delete token for ${request.provider} from database via removeProviderToken:`, error);
|
|
7414
7840
|
}
|