integrate-sdk 0.3.10 → 0.3.11
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
|
@@ -608,6 +608,7 @@ class OAuthWindowManager {
|
|
|
608
608
|
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
609
609
|
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
610
610
|
const features = [
|
|
611
|
+
`popup=yes`,
|
|
611
612
|
`width=${width}`,
|
|
612
613
|
`height=${height}`,
|
|
613
614
|
`left=${left}`,
|
|
@@ -619,9 +620,11 @@ class OAuthWindowManager {
|
|
|
619
620
|
"menubar=no",
|
|
620
621
|
"scrollbars=yes",
|
|
621
622
|
"resizable=yes",
|
|
622
|
-
"copyhistory=no"
|
|
623
|
+
"copyhistory=no",
|
|
624
|
+
"noopener=no"
|
|
623
625
|
].join(",");
|
|
624
|
-
|
|
626
|
+
const windowName = `oauth_popup_${Date.now()}`;
|
|
627
|
+
this.popupWindow = window.open(url, windowName, features);
|
|
625
628
|
if (!this.popupWindow) {
|
|
626
629
|
console.warn("Popup was blocked by the browser. Please allow popups for this site.");
|
|
627
630
|
return null;
|
|
@@ -790,6 +793,7 @@ class OAuthManager {
|
|
|
790
793
|
popupOptions: flowConfig?.popupOptions,
|
|
791
794
|
onAuthCallback: flowConfig?.onAuthCallback
|
|
792
795
|
};
|
|
796
|
+
this.cleanupExpiredPendingAuths();
|
|
793
797
|
}
|
|
794
798
|
async initiateFlow(provider, config) {
|
|
795
799
|
const codeVerifier = generateCodeVerifier();
|
|
@@ -805,6 +809,7 @@ class OAuthManager {
|
|
|
805
809
|
initiatedAt: Date.now()
|
|
806
810
|
};
|
|
807
811
|
this.pendingAuths.set(state, pendingAuth);
|
|
812
|
+
this.savePendingAuthToStorage(state, pendingAuth);
|
|
808
813
|
const authUrl = await this.getAuthorizationUrl(provider, config.scopes, state, codeChallenge, config.redirectUri);
|
|
809
814
|
if (this.flowConfig.mode === "popup") {
|
|
810
815
|
this.windowManager.openPopup(authUrl, this.flowConfig.popupOptions);
|
|
@@ -820,13 +825,17 @@ class OAuthManager {
|
|
|
820
825
|
}
|
|
821
826
|
}
|
|
822
827
|
async handleCallback(code, state) {
|
|
823
|
-
|
|
828
|
+
let pendingAuth = this.pendingAuths.get(state);
|
|
829
|
+
if (!pendingAuth) {
|
|
830
|
+
pendingAuth = this.loadPendingAuthFromStorage(state);
|
|
831
|
+
}
|
|
824
832
|
if (!pendingAuth) {
|
|
825
833
|
throw new Error("Invalid state parameter: no matching OAuth flow found");
|
|
826
834
|
}
|
|
827
835
|
const fiveMinutes = 5 * 60 * 1000;
|
|
828
836
|
if (Date.now() - pendingAuth.initiatedAt > fiveMinutes) {
|
|
829
837
|
this.pendingAuths.delete(state);
|
|
838
|
+
this.removePendingAuthFromStorage(state);
|
|
830
839
|
throw new Error("OAuth flow expired: please try again");
|
|
831
840
|
}
|
|
832
841
|
if (this.flowConfig.onAuthCallback) {
|
|
@@ -841,9 +850,11 @@ class OAuthManager {
|
|
|
841
850
|
this.sessionToken = response.sessionToken;
|
|
842
851
|
this.saveSessionToken(response.sessionToken);
|
|
843
852
|
this.pendingAuths.delete(state);
|
|
853
|
+
this.removePendingAuthFromStorage(state);
|
|
844
854
|
return response.sessionToken;
|
|
845
855
|
} catch (error) {
|
|
846
856
|
this.pendingAuths.delete(state);
|
|
857
|
+
this.removePendingAuthFromStorage(state);
|
|
847
858
|
throw error;
|
|
848
859
|
}
|
|
849
860
|
}
|
|
@@ -904,6 +915,69 @@ class OAuthManager {
|
|
|
904
915
|
}
|
|
905
916
|
}
|
|
906
917
|
}
|
|
918
|
+
savePendingAuthToStorage(state, pendingAuth) {
|
|
919
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
920
|
+
try {
|
|
921
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
922
|
+
window.localStorage.setItem(key, JSON.stringify(pendingAuth));
|
|
923
|
+
} catch (error) {
|
|
924
|
+
console.error("Failed to save pending auth to localStorage:", error);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
loadPendingAuthFromStorage(state) {
|
|
929
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
930
|
+
try {
|
|
931
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
932
|
+
const stored = window.localStorage.getItem(key);
|
|
933
|
+
if (stored) {
|
|
934
|
+
return JSON.parse(stored);
|
|
935
|
+
}
|
|
936
|
+
} catch (error) {
|
|
937
|
+
console.error("Failed to load pending auth from localStorage:", error);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
removePendingAuthFromStorage(state) {
|
|
943
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
944
|
+
try {
|
|
945
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
946
|
+
window.localStorage.removeItem(key);
|
|
947
|
+
} catch (error) {
|
|
948
|
+
console.error("Failed to remove pending auth from localStorage:", error);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
cleanupExpiredPendingAuths() {
|
|
953
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
954
|
+
try {
|
|
955
|
+
const prefix = "integrate_oauth_pending_";
|
|
956
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
957
|
+
const now = Date.now();
|
|
958
|
+
const keysToRemove = [];
|
|
959
|
+
for (let i = 0;i < window.localStorage.length; i++) {
|
|
960
|
+
const key = window.localStorage.key(i);
|
|
961
|
+
if (key && key.startsWith(prefix)) {
|
|
962
|
+
try {
|
|
963
|
+
const stored = window.localStorage.getItem(key);
|
|
964
|
+
if (stored) {
|
|
965
|
+
const pendingAuth = JSON.parse(stored);
|
|
966
|
+
if (now - pendingAuth.initiatedAt > fiveMinutes) {
|
|
967
|
+
keysToRemove.push(key);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
} catch (error) {
|
|
971
|
+
keysToRemove.push(key);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
keysToRemove.forEach((key) => window.localStorage.removeItem(key));
|
|
976
|
+
} catch (error) {
|
|
977
|
+
console.error("Failed to cleanup expired pending auths:", error);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
907
981
|
async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
|
|
908
982
|
const url = `${this.oauthApiBase}/authorize`;
|
|
909
983
|
const response = await fetch(url, {
|
package/dist/server.js
CHANGED
|
@@ -608,6 +608,7 @@ class OAuthWindowManager {
|
|
|
608
608
|
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
609
609
|
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
610
610
|
const features = [
|
|
611
|
+
`popup=yes`,
|
|
611
612
|
`width=${width}`,
|
|
612
613
|
`height=${height}`,
|
|
613
614
|
`left=${left}`,
|
|
@@ -619,9 +620,11 @@ class OAuthWindowManager {
|
|
|
619
620
|
"menubar=no",
|
|
620
621
|
"scrollbars=yes",
|
|
621
622
|
"resizable=yes",
|
|
622
|
-
"copyhistory=no"
|
|
623
|
+
"copyhistory=no",
|
|
624
|
+
"noopener=no"
|
|
623
625
|
].join(",");
|
|
624
|
-
|
|
626
|
+
const windowName = `oauth_popup_${Date.now()}`;
|
|
627
|
+
this.popupWindow = window.open(url, windowName, features);
|
|
625
628
|
if (!this.popupWindow) {
|
|
626
629
|
console.warn("Popup was blocked by the browser. Please allow popups for this site.");
|
|
627
630
|
return null;
|
|
@@ -790,6 +793,7 @@ class OAuthManager {
|
|
|
790
793
|
popupOptions: flowConfig?.popupOptions,
|
|
791
794
|
onAuthCallback: flowConfig?.onAuthCallback
|
|
792
795
|
};
|
|
796
|
+
this.cleanupExpiredPendingAuths();
|
|
793
797
|
}
|
|
794
798
|
async initiateFlow(provider, config) {
|
|
795
799
|
const codeVerifier = generateCodeVerifier();
|
|
@@ -805,6 +809,7 @@ class OAuthManager {
|
|
|
805
809
|
initiatedAt: Date.now()
|
|
806
810
|
};
|
|
807
811
|
this.pendingAuths.set(state, pendingAuth);
|
|
812
|
+
this.savePendingAuthToStorage(state, pendingAuth);
|
|
808
813
|
const authUrl = await this.getAuthorizationUrl(provider, config.scopes, state, codeChallenge, config.redirectUri);
|
|
809
814
|
if (this.flowConfig.mode === "popup") {
|
|
810
815
|
this.windowManager.openPopup(authUrl, this.flowConfig.popupOptions);
|
|
@@ -820,13 +825,17 @@ class OAuthManager {
|
|
|
820
825
|
}
|
|
821
826
|
}
|
|
822
827
|
async handleCallback(code, state) {
|
|
823
|
-
|
|
828
|
+
let pendingAuth = this.pendingAuths.get(state);
|
|
829
|
+
if (!pendingAuth) {
|
|
830
|
+
pendingAuth = this.loadPendingAuthFromStorage(state);
|
|
831
|
+
}
|
|
824
832
|
if (!pendingAuth) {
|
|
825
833
|
throw new Error("Invalid state parameter: no matching OAuth flow found");
|
|
826
834
|
}
|
|
827
835
|
const fiveMinutes = 5 * 60 * 1000;
|
|
828
836
|
if (Date.now() - pendingAuth.initiatedAt > fiveMinutes) {
|
|
829
837
|
this.pendingAuths.delete(state);
|
|
838
|
+
this.removePendingAuthFromStorage(state);
|
|
830
839
|
throw new Error("OAuth flow expired: please try again");
|
|
831
840
|
}
|
|
832
841
|
if (this.flowConfig.onAuthCallback) {
|
|
@@ -841,9 +850,11 @@ class OAuthManager {
|
|
|
841
850
|
this.sessionToken = response.sessionToken;
|
|
842
851
|
this.saveSessionToken(response.sessionToken);
|
|
843
852
|
this.pendingAuths.delete(state);
|
|
853
|
+
this.removePendingAuthFromStorage(state);
|
|
844
854
|
return response.sessionToken;
|
|
845
855
|
} catch (error) {
|
|
846
856
|
this.pendingAuths.delete(state);
|
|
857
|
+
this.removePendingAuthFromStorage(state);
|
|
847
858
|
throw error;
|
|
848
859
|
}
|
|
849
860
|
}
|
|
@@ -904,6 +915,69 @@ class OAuthManager {
|
|
|
904
915
|
}
|
|
905
916
|
}
|
|
906
917
|
}
|
|
918
|
+
savePendingAuthToStorage(state, pendingAuth) {
|
|
919
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
920
|
+
try {
|
|
921
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
922
|
+
window.localStorage.setItem(key, JSON.stringify(pendingAuth));
|
|
923
|
+
} catch (error) {
|
|
924
|
+
console.error("Failed to save pending auth to localStorage:", error);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
loadPendingAuthFromStorage(state) {
|
|
929
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
930
|
+
try {
|
|
931
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
932
|
+
const stored = window.localStorage.getItem(key);
|
|
933
|
+
if (stored) {
|
|
934
|
+
return JSON.parse(stored);
|
|
935
|
+
}
|
|
936
|
+
} catch (error) {
|
|
937
|
+
console.error("Failed to load pending auth from localStorage:", error);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
removePendingAuthFromStorage(state) {
|
|
943
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
944
|
+
try {
|
|
945
|
+
const key = `integrate_oauth_pending_${state}`;
|
|
946
|
+
window.localStorage.removeItem(key);
|
|
947
|
+
} catch (error) {
|
|
948
|
+
console.error("Failed to remove pending auth from localStorage:", error);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
cleanupExpiredPendingAuths() {
|
|
953
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
954
|
+
try {
|
|
955
|
+
const prefix = "integrate_oauth_pending_";
|
|
956
|
+
const fiveMinutes = 5 * 60 * 1000;
|
|
957
|
+
const now = Date.now();
|
|
958
|
+
const keysToRemove = [];
|
|
959
|
+
for (let i = 0;i < window.localStorage.length; i++) {
|
|
960
|
+
const key = window.localStorage.key(i);
|
|
961
|
+
if (key && key.startsWith(prefix)) {
|
|
962
|
+
try {
|
|
963
|
+
const stored = window.localStorage.getItem(key);
|
|
964
|
+
if (stored) {
|
|
965
|
+
const pendingAuth = JSON.parse(stored);
|
|
966
|
+
if (now - pendingAuth.initiatedAt > fiveMinutes) {
|
|
967
|
+
keysToRemove.push(key);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
} catch (error) {
|
|
971
|
+
keysToRemove.push(key);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
keysToRemove.forEach((key) => window.localStorage.removeItem(key));
|
|
976
|
+
} catch (error) {
|
|
977
|
+
console.error("Failed to cleanup expired pending auths:", error);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
907
981
|
async getAuthorizationUrl(provider, scopes, state, codeChallenge, redirectUri) {
|
|
908
982
|
const url = `${this.oauthApiBase}/authorize`;
|
|
909
983
|
const response = await fetch(url, {
|
|
@@ -79,6 +79,27 @@ export declare class OAuthManager {
|
|
|
79
79
|
* Save session token to sessionStorage
|
|
80
80
|
*/
|
|
81
81
|
private saveSessionToken;
|
|
82
|
+
/**
|
|
83
|
+
* Save pending auth to localStorage (for redirect flows)
|
|
84
|
+
* Uses localStorage instead of sessionStorage because OAuth may open in a new tab,
|
|
85
|
+
* and sessionStorage is isolated per tab. localStorage is shared across tabs.
|
|
86
|
+
* Keyed by state parameter for security and retrieval.
|
|
87
|
+
*/
|
|
88
|
+
private savePendingAuthToStorage;
|
|
89
|
+
/**
|
|
90
|
+
* Load pending auth from localStorage (after redirect)
|
|
91
|
+
* Returns undefined if not found or invalid
|
|
92
|
+
*/
|
|
93
|
+
private loadPendingAuthFromStorage;
|
|
94
|
+
/**
|
|
95
|
+
* Remove pending auth from localStorage
|
|
96
|
+
*/
|
|
97
|
+
private removePendingAuthFromStorage;
|
|
98
|
+
/**
|
|
99
|
+
* Clean up expired pending auth entries from localStorage
|
|
100
|
+
* Removes any entries older than 5 minutes
|
|
101
|
+
*/
|
|
102
|
+
private cleanupExpiredPendingAuths;
|
|
82
103
|
/**
|
|
83
104
|
* Request authorization URL from user's API route
|
|
84
105
|
* The API route will add OAuth secrets and forward to MCP server
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/oauth/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EACV,eAAe,EAEf,UAAU,EAGX,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,YAAY,CAAS;gBAG3B,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/oauth/manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EACV,eAAe,EAEf,UAAU,EAGX,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,YAAY,CAAS;gBAG3B,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAcvC;;;;;;;;;;;;;;;;OAgBG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CxE;;;;;;;;;;;;;OAaG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyDlE;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAoC5D;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKpC;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAWzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;IAWhC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAelC;;OAEG;IACH,OAAO,CAAC,4BAA4B;IAWpC;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAoClC;;;OAGG;YACW,mBAAmB;IAiCjC;;;OAGG;YACW,oBAAoB;IA8BlC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-manager.d.ts","sourceRoot":"","sources":["../../../src/oauth/window-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AASpE;;;;;;GAMG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,kBAAkB,CAA+C;IACzE,OAAO,CAAC,iBAAiB,CAA8C;IAEvE;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"window-manager.d.ts","sourceRoot":"","sources":["../../../src/oauth/window-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AASpE;;;;;;GAMG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,kBAAkB,CAA+C;IACzE,OAAO,CAAC,iBAAiB,CAA8C;IAEvE;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;IA6C7D;;;;;;;;;;OAUG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ/B;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB,CACf,IAAI,EAAE,OAAO,GAAG,UAAU,EAC1B,SAAS,GAAE,MAAsB,GAChC,OAAO,CAAC,mBAAmB,CAAC;IAQ/B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkE9B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAiEjC;;OAEG;IACH,OAAO,CAAC,OAAO;IAiBf;;;OAGG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,GAAG,IAAI,CAwBP"}
|