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/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
|
-
|
|
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.
|
|
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
|
|
1111
|
+
console.error(`Failed to delete tokens for ${provider} from database:`, error);
|
|
809
1112
|
}
|
|
810
1113
|
} else if (this.setTokenCallback) {
|
|
811
1114
|
try {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
}
|
|
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.
|
|
866
|
-
|
|
1211
|
+
if (!this.setTokenCallback && !this.removeTokenCallback && !this.skipLocalStorage) {
|
|
1212
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
867
1213
|
try {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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 (
|
|
904
|
-
this.clearProviderToken(provider);
|
|
1257
|
+
if (this.skipLocalStorage) {
|
|
905
1258
|
return;
|
|
906
1259
|
}
|
|
907
|
-
if (
|
|
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
|
-
|
|
1283
|
+
const tokenEmail = email || tokenData.email;
|
|
1284
|
+
if (tokenEmail) {
|
|
911
1285
|
try {
|
|
912
|
-
|
|
913
|
-
window.localStorage.setItem(key, JSON.stringify(tokenData));
|
|
1286
|
+
await this.indexedDBStorage.saveToken(provider, tokenEmail, tokenData);
|
|
914
1287
|
} catch (error) {
|
|
915
|
-
|
|
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 (
|
|
1322
|
+
if (email) {
|
|
932
1323
|
try {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
if (
|
|
936
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
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.
|
|
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.
|
|
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
|
}
|