@mcp-use/client 2.0.0-beta.12 → 2.0.0-beta.13
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/.tsbuildinfo +1 -1
- package/dist/auth/browser.d.ts +2 -0
- package/dist/auth/browser.d.ts.map +1 -1
- package/dist/auth/storage.d.ts +12 -3
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/index-browser.js +215 -81
- package/dist/index-browser.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react/McpClientProvider.d.ts.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +361 -159
- package/dist/react/index.js.map +1 -1
- package/dist/react/storage.d.ts +10 -10
- package/dist/react/storage.d.ts.map +1 -1
- package/dist/react/types.d.ts +22 -5
- package/dist/react/types.d.ts.map +1 -1
- package/dist/react/useMcp-operations.d.ts.map +1 -1
- package/dist/react/view/inject-openai-file-apis.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/auth/browser.d.ts
CHANGED
|
@@ -41,11 +41,13 @@ export declare class BrowserOAuthClientProvider implements OAuthClientProvider {
|
|
|
41
41
|
readonly serverUrl: string;
|
|
42
42
|
readonly staticClientInfo?: OAuthClientInformation;
|
|
43
43
|
private session;
|
|
44
|
+
private readonly storage;
|
|
44
45
|
readonly preventAutoAuth?: boolean;
|
|
45
46
|
private useRedirectFlow?;
|
|
46
47
|
private oauthProxyUrl?;
|
|
47
48
|
private connectionUrl?;
|
|
48
49
|
private proxyOAuthRequests;
|
|
50
|
+
private lastAttemptedAuthUrl;
|
|
49
51
|
readonly onPopupWindow: ((url: string, features: string, window: globalThis.Window | null) => void) | undefined;
|
|
50
52
|
constructor(serverUrl: string, options?: BrowserOAuthOptions);
|
|
51
53
|
get storageKeyPrefix(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/auth/browser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EACtB,6BAA6B,EAC7B,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACZ,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/auth/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/storage.d.ts
CHANGED
|
@@ -12,14 +12,23 @@ export interface KVStore {
|
|
|
12
12
|
keys(): Promise<string[]> | string[];
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* `KVStore`
|
|
15
|
+
* Encrypted `KVStore` backed by `globalThis.localStorage`.
|
|
16
|
+
*
|
|
17
|
+
* Values use AES-256-GCM with a non-extractable origin key held by IndexedDB.
|
|
18
|
+
* Legacy plaintext values are encrypted on first read. When durable browser
|
|
19
|
+
* cryptography is unavailable, the store removes plaintext and falls back to
|
|
20
|
+
* memory for the lifetime of this instance.
|
|
16
21
|
*
|
|
17
22
|
* @internal
|
|
18
23
|
*/
|
|
19
24
|
export declare class LocalStorageKVStore implements KVStore {
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
private readonly fallback;
|
|
26
|
+
private keyPromise;
|
|
27
|
+
private durable;
|
|
28
|
+
get(key: string): Promise<string | null>;
|
|
29
|
+
set(key: string, value: string): Promise<void>;
|
|
22
30
|
remove(key: string): void;
|
|
23
31
|
keys(): string[];
|
|
32
|
+
private getCryptoKey;
|
|
24
33
|
}
|
|
25
34
|
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IACzD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IACzD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC;CACtC;AAeD;;;;;;;;;GASG;AACH,qBAAa,mBAAoB,YAAW,OAAO;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,OAAO,CAAQ;IAEjB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAoCxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IASzB,IAAI,IAAI,MAAM,EAAE;IAehB,OAAO,CAAC,YAAY;CAIrB"}
|
package/dist/index-browser.js
CHANGED
|
@@ -128,7 +128,7 @@ var Logger = class {
|
|
|
128
128
|
var logger = Logger.get();
|
|
129
129
|
|
|
130
130
|
// src/utils/version.ts
|
|
131
|
-
var VERSION = "2.0.0-beta.
|
|
131
|
+
var VERSION = "2.0.0-beta.13";
|
|
132
132
|
function getPackageVersion() {
|
|
133
133
|
return VERSION;
|
|
134
134
|
}
|
|
@@ -589,6 +589,186 @@ setConnectorTelemetryTracker(
|
|
|
589
589
|
(data) => Telemetry.getInstance().trackConnectorInit(data).catch(() => void 0)
|
|
590
590
|
);
|
|
591
591
|
|
|
592
|
+
// src/auth/storage.ts
|
|
593
|
+
var AUTH_CRYPTO_DATABASE = "mcp-use-oauth-crypto";
|
|
594
|
+
var AUTH_CRYPTO_STORE = "keys";
|
|
595
|
+
var AUTH_CRYPTO_KEY = "aes-gcm-v1";
|
|
596
|
+
var textEncoder = new TextEncoder();
|
|
597
|
+
var textDecoder = new TextDecoder();
|
|
598
|
+
var LocalStorageKVStore = class {
|
|
599
|
+
fallback = /* @__PURE__ */ new Map();
|
|
600
|
+
keyPromise;
|
|
601
|
+
durable = true;
|
|
602
|
+
async get(key) {
|
|
603
|
+
if (!this.durable) return this.fallback.get(key) ?? null;
|
|
604
|
+
let stored;
|
|
605
|
+
try {
|
|
606
|
+
stored = localStorage.getItem(key);
|
|
607
|
+
} catch {
|
|
608
|
+
this.durable = false;
|
|
609
|
+
return this.fallback.get(key) ?? null;
|
|
610
|
+
}
|
|
611
|
+
if (stored === null) return null;
|
|
612
|
+
const envelope = parseEncryptedEnvelope(stored);
|
|
613
|
+
if (!envelope) {
|
|
614
|
+
await this.set(key, stored);
|
|
615
|
+
return stored;
|
|
616
|
+
}
|
|
617
|
+
try {
|
|
618
|
+
const cryptoKey = await this.getCryptoKey();
|
|
619
|
+
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
620
|
+
{
|
|
621
|
+
name: "AES-GCM",
|
|
622
|
+
iv: decodeBase64(envelope.iv),
|
|
623
|
+
additionalData: textEncoder.encode(key)
|
|
624
|
+
},
|
|
625
|
+
cryptoKey,
|
|
626
|
+
decodeBase64(envelope.ciphertext)
|
|
627
|
+
);
|
|
628
|
+
return textDecoder.decode(plaintext);
|
|
629
|
+
} catch {
|
|
630
|
+
await this.remove(key);
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async set(key, value) {
|
|
635
|
+
if (!this.durable) {
|
|
636
|
+
this.fallback.set(key, value);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
try {
|
|
640
|
+
const cryptoKey = await this.getCryptoKey();
|
|
641
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
642
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
643
|
+
{
|
|
644
|
+
name: "AES-GCM",
|
|
645
|
+
iv,
|
|
646
|
+
additionalData: textEncoder.encode(key)
|
|
647
|
+
},
|
|
648
|
+
cryptoKey,
|
|
649
|
+
textEncoder.encode(value)
|
|
650
|
+
);
|
|
651
|
+
const envelope = {
|
|
652
|
+
v: 1,
|
|
653
|
+
alg: "A256GCM",
|
|
654
|
+
iv: encodeBase64(iv),
|
|
655
|
+
ciphertext: encodeBase64(new Uint8Array(ciphertext))
|
|
656
|
+
};
|
|
657
|
+
localStorage.setItem(key, JSON.stringify(envelope));
|
|
658
|
+
this.fallback.delete(key);
|
|
659
|
+
} catch {
|
|
660
|
+
this.durable = false;
|
|
661
|
+
try {
|
|
662
|
+
localStorage.removeItem(key);
|
|
663
|
+
} catch {
|
|
664
|
+
}
|
|
665
|
+
this.fallback.set(key, value);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
remove(key) {
|
|
669
|
+
this.fallback.delete(key);
|
|
670
|
+
try {
|
|
671
|
+
localStorage.removeItem(key);
|
|
672
|
+
} catch {
|
|
673
|
+
this.durable = false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
keys() {
|
|
677
|
+
const out = new Set(this.fallback.keys());
|
|
678
|
+
if (this.durable) {
|
|
679
|
+
try {
|
|
680
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
681
|
+
const key = localStorage.key(i);
|
|
682
|
+
if (key) out.add(key);
|
|
683
|
+
}
|
|
684
|
+
} catch {
|
|
685
|
+
this.durable = false;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
return [...out];
|
|
689
|
+
}
|
|
690
|
+
getCryptoKey() {
|
|
691
|
+
this.keyPromise ??= getOrCreateCryptoKey();
|
|
692
|
+
return this.keyPromise;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
function parseEncryptedEnvelope(value) {
|
|
696
|
+
try {
|
|
697
|
+
const parsed = JSON.parse(value);
|
|
698
|
+
if (!parsed || typeof parsed !== "object" || !("v" in parsed) || parsed.v !== 1 || !("alg" in parsed) || parsed.alg !== "A256GCM" || !("iv" in parsed) || typeof parsed.iv !== "string" || !("ciphertext" in parsed) || typeof parsed.ciphertext !== "string") {
|
|
699
|
+
return void 0;
|
|
700
|
+
}
|
|
701
|
+
return parsed;
|
|
702
|
+
} catch {
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
async function getOrCreateCryptoKey() {
|
|
707
|
+
if (!globalThis.crypto?.subtle || typeof indexedDB === "undefined") {
|
|
708
|
+
throw new Error("Durable browser cryptography is unavailable");
|
|
709
|
+
}
|
|
710
|
+
const candidate = await globalThis.crypto.subtle.generateKey(
|
|
711
|
+
{ name: "AES-GCM", length: 256 },
|
|
712
|
+
false,
|
|
713
|
+
["encrypt", "decrypt"]
|
|
714
|
+
);
|
|
715
|
+
const database = await openCryptoDatabase();
|
|
716
|
+
try {
|
|
717
|
+
return await new Promise((resolve, reject) => {
|
|
718
|
+
const transaction = database.transaction(AUTH_CRYPTO_STORE, "readwrite");
|
|
719
|
+
const store = transaction.objectStore(AUTH_CRYPTO_STORE);
|
|
720
|
+
const request = store.get(AUTH_CRYPTO_KEY);
|
|
721
|
+
let selected;
|
|
722
|
+
request.onsuccess = () => {
|
|
723
|
+
selected = request.result;
|
|
724
|
+
if (!selected) {
|
|
725
|
+
selected = candidate;
|
|
726
|
+
store.put(candidate, AUTH_CRYPTO_KEY);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
request.onerror = () => reject(request.error);
|
|
730
|
+
transaction.oncomplete = () => {
|
|
731
|
+
if (selected) resolve(selected);
|
|
732
|
+
else reject(new Error("OAuth encryption key was not initialized"));
|
|
733
|
+
};
|
|
734
|
+
transaction.onerror = () => reject(transaction.error);
|
|
735
|
+
transaction.onabort = () => reject(transaction.error);
|
|
736
|
+
});
|
|
737
|
+
} finally {
|
|
738
|
+
database.close();
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function openCryptoDatabase() {
|
|
742
|
+
return new Promise((resolve, reject) => {
|
|
743
|
+
const request = indexedDB.open(AUTH_CRYPTO_DATABASE, 1);
|
|
744
|
+
request.onupgradeneeded = () => {
|
|
745
|
+
const database = request.result;
|
|
746
|
+
if (!database.objectStoreNames.contains(AUTH_CRYPTO_STORE)) {
|
|
747
|
+
database.createObjectStore(AUTH_CRYPTO_STORE);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
request.onsuccess = () => resolve(request.result);
|
|
751
|
+
request.onerror = () => reject(request.error);
|
|
752
|
+
request.onblocked = () => reject(new Error("OAuth encryption database is blocked"));
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
function encodeBase64(bytes) {
|
|
756
|
+
let binary = "";
|
|
757
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
758
|
+
return btoa(binary);
|
|
759
|
+
}
|
|
760
|
+
function decodeBase64(value) {
|
|
761
|
+
const binary = atob(value);
|
|
762
|
+
const bytes = new Uint8Array(binary.length);
|
|
763
|
+
for (let index = 0; index < binary.length; index++) {
|
|
764
|
+
bytes[index] = binary.charCodeAt(index);
|
|
765
|
+
}
|
|
766
|
+
return bytes;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// src/auth/session-store.ts
|
|
770
|
+
import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
|
|
771
|
+
|
|
592
772
|
// src/auth/url.ts
|
|
593
773
|
function sanitizeUrl(raw) {
|
|
594
774
|
const abort = () => {
|
|
@@ -613,29 +793,7 @@ function sanitizeParam([k, v]) {
|
|
|
613
793
|
return `${encodeURIComponent(k)}${v.length > 0 ? `=${encodeURIComponent(v)}` : ""}`;
|
|
614
794
|
}
|
|
615
795
|
|
|
616
|
-
// src/auth/storage.ts
|
|
617
|
-
var LocalStorageKVStore = class {
|
|
618
|
-
get(key) {
|
|
619
|
-
return localStorage.getItem(key);
|
|
620
|
-
}
|
|
621
|
-
set(key, value) {
|
|
622
|
-
localStorage.setItem(key, value);
|
|
623
|
-
}
|
|
624
|
-
remove(key) {
|
|
625
|
-
localStorage.removeItem(key);
|
|
626
|
-
}
|
|
627
|
-
keys() {
|
|
628
|
-
const out = [];
|
|
629
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
630
|
-
const k = localStorage.key(i);
|
|
631
|
-
if (k) out.push(k);
|
|
632
|
-
}
|
|
633
|
-
return out;
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
|
|
637
796
|
// src/auth/session-store.ts
|
|
638
|
-
import { validateClientMetadataUrl } from "@modelcontextprotocol/client";
|
|
639
797
|
var OAuthSessionStore = class _OAuthSessionStore {
|
|
640
798
|
serverUrl;
|
|
641
799
|
storageKeyPrefix;
|
|
@@ -947,16 +1105,25 @@ async function serializeBody(body) {
|
|
|
947
1105
|
if (body instanceof Blob) return await body.text();
|
|
948
1106
|
return body;
|
|
949
1107
|
}
|
|
1108
|
+
function trimTrailingSlashes(value) {
|
|
1109
|
+
let end = value.length;
|
|
1110
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) {
|
|
1111
|
+
end--;
|
|
1112
|
+
}
|
|
1113
|
+
return value.slice(0, end);
|
|
1114
|
+
}
|
|
950
1115
|
var BrowserOAuthClientProvider = class {
|
|
951
1116
|
serverUrl;
|
|
952
1117
|
staticClientInfo;
|
|
953
1118
|
session;
|
|
1119
|
+
storage;
|
|
954
1120
|
// Browser-only state
|
|
955
1121
|
preventAutoAuth;
|
|
956
1122
|
useRedirectFlow;
|
|
957
1123
|
oauthProxyUrl;
|
|
958
1124
|
connectionUrl;
|
|
959
1125
|
proxyOAuthRequests;
|
|
1126
|
+
lastAttemptedAuthUrl = null;
|
|
960
1127
|
onPopupWindow;
|
|
961
1128
|
constructor(serverUrl, options = {}) {
|
|
962
1129
|
if (options.staticClientInfo?.client_secret) {
|
|
@@ -965,10 +1132,11 @@ var BrowserOAuthClientProvider = class {
|
|
|
965
1132
|
);
|
|
966
1133
|
}
|
|
967
1134
|
this.serverUrl = serverUrl;
|
|
1135
|
+
this.storage = new LocalStorageKVStore();
|
|
968
1136
|
this.session = new OAuthSessionStore(
|
|
969
1137
|
serverUrl,
|
|
970
1138
|
{ ...options, allowClientSecret: false },
|
|
971
|
-
|
|
1139
|
+
this.storage
|
|
972
1140
|
);
|
|
973
1141
|
this.preventAutoAuth = options.preventAutoAuth;
|
|
974
1142
|
this.useRedirectFlow = options.useRedirectFlow;
|
|
@@ -1033,8 +1201,8 @@ var BrowserOAuthClientProvider = class {
|
|
|
1033
1201
|
const [doc, ...suffixParts] = rest.split("/");
|
|
1034
1202
|
if (!doc) return url;
|
|
1035
1203
|
const suffix = suffixParts.length ? `/${suffixParts.join("/")}` : "";
|
|
1036
|
-
const connectionPath = connection.pathname
|
|
1037
|
-
const targetPath = target.pathname
|
|
1204
|
+
const connectionPath = trimTrailingSlashes(connection.pathname);
|
|
1205
|
+
const targetPath = trimTrailingSlashes(target.pathname);
|
|
1038
1206
|
const newSuffix = suffix && suffix === connectionPath ? targetPath : suffix;
|
|
1039
1207
|
return `${target.origin}/.well-known/${doc}${newSuffix}${requested.search}`;
|
|
1040
1208
|
} catch {
|
|
@@ -1185,6 +1353,7 @@ var BrowserOAuthClientProvider = class {
|
|
|
1185
1353
|
return this.session.tokens(ctx);
|
|
1186
1354
|
}
|
|
1187
1355
|
saveTokens(tokens, ctx) {
|
|
1356
|
+
this.lastAttemptedAuthUrl = null;
|
|
1188
1357
|
return this.session.saveTokens(tokens, ctx);
|
|
1189
1358
|
}
|
|
1190
1359
|
async clientInformation(ctx) {
|
|
@@ -1251,16 +1420,21 @@ var BrowserOAuthClientProvider = class {
|
|
|
1251
1420
|
* use `redirectToAuthorization` for that.
|
|
1252
1421
|
*/
|
|
1253
1422
|
async prepareAuthorizationUrl(authorizationUrl) {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1423
|
+
const prepared = await this.session.storeAuthorizationState(
|
|
1424
|
+
authorizationUrl,
|
|
1425
|
+
{
|
|
1426
|
+
extraProviderOptions: {
|
|
1427
|
+
oauthProxyUrl: this.oauthProxyUrl,
|
|
1428
|
+
...this.clientMetadataUrl ? { clientMetadataUrl: this.clientMetadataUrl } : {},
|
|
1429
|
+
...this.staticClientInfo ? { staticClientInfo: this.staticClientInfo } : {},
|
|
1430
|
+
...this.scope ? { scope: this.scope } : {}
|
|
1431
|
+
},
|
|
1432
|
+
flowType: this.useRedirectFlow ? "redirect" : "popup",
|
|
1433
|
+
returnUrl: typeof window !== "undefined" ? window.location.href : void 0
|
|
1434
|
+
}
|
|
1435
|
+
);
|
|
1436
|
+
this.lastAttemptedAuthUrl = prepared;
|
|
1437
|
+
return prepared;
|
|
1264
1438
|
}
|
|
1265
1439
|
/**
|
|
1266
1440
|
* Redirects the user agent to the authorization URL, storing necessary state.
|
|
@@ -1312,64 +1486,24 @@ var BrowserOAuthClientProvider = class {
|
|
|
1312
1486
|
* Retrieves the last URL passed to `redirectToAuthorization`. Useful for manual fallback.
|
|
1313
1487
|
*/
|
|
1314
1488
|
getLastAttemptedAuthUrl() {
|
|
1315
|
-
|
|
1316
|
-
if (!storedUrl) return null;
|
|
1317
|
-
const storedCallbackUrl = localStorage.getItem(
|
|
1318
|
-
this.getKey("last_auth_callback_url")
|
|
1319
|
-
);
|
|
1320
|
-
if (storedCallbackUrl !== this.callbackUrl) {
|
|
1321
|
-
console.info(
|
|
1322
|
-
`[${this.storageKeyPrefix}] Recovering stale OAuth state whose callback cannot be verified.`
|
|
1323
|
-
);
|
|
1324
|
-
this.clearStorage();
|
|
1325
|
-
return null;
|
|
1326
|
-
}
|
|
1327
|
-
const sanitized = sanitizeUrl(storedUrl);
|
|
1328
|
-
try {
|
|
1329
|
-
const redirectUri = new URL(sanitized).searchParams.get("redirect_uri");
|
|
1330
|
-
if (redirectUri && new URL(redirectUri).toString() !== new URL(this.callbackUrl).toString()) {
|
|
1331
|
-
console.info(
|
|
1332
|
-
`[${this.storageKeyPrefix}] Recovering stale OAuth state after the Inspector callback path changed.`
|
|
1333
|
-
);
|
|
1334
|
-
this.clearStorage();
|
|
1335
|
-
return null;
|
|
1336
|
-
}
|
|
1337
|
-
} catch {
|
|
1338
|
-
this.clearStorage();
|
|
1339
|
-
return null;
|
|
1340
|
-
}
|
|
1341
|
-
return sanitized;
|
|
1489
|
+
return this.lastAttemptedAuthUrl;
|
|
1342
1490
|
}
|
|
1343
1491
|
clearStorage() {
|
|
1492
|
+
this.lastAttemptedAuthUrl = null;
|
|
1344
1493
|
const prefixPattern = `${this.storageKeyPrefix}_${this.serverUrlHash}_`;
|
|
1345
1494
|
const statePattern = `${this.storageKeyPrefix}:state_`;
|
|
1346
1495
|
const keysToRemove = [];
|
|
1347
1496
|
let count = 0;
|
|
1348
|
-
for (
|
|
1349
|
-
const key = localStorage.key(i);
|
|
1350
|
-
if (!key) continue;
|
|
1497
|
+
for (const key of this.storage.keys()) {
|
|
1351
1498
|
if (key.startsWith(prefixPattern)) {
|
|
1352
1499
|
keysToRemove.push(key);
|
|
1353
1500
|
} else if (key.startsWith(statePattern)) {
|
|
1354
|
-
|
|
1355
|
-
const item = localStorage.getItem(key);
|
|
1356
|
-
if (item) {
|
|
1357
|
-
const state = JSON.parse(item);
|
|
1358
|
-
if (state.serverUrlHash === this.serverUrlHash) {
|
|
1359
|
-
keysToRemove.push(key);
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
} catch (e) {
|
|
1363
|
-
console.warn(
|
|
1364
|
-
`[${this.storageKeyPrefix}] Error parsing state key ${key} during clearStorage:`,
|
|
1365
|
-
e
|
|
1366
|
-
);
|
|
1367
|
-
}
|
|
1501
|
+
keysToRemove.push(key);
|
|
1368
1502
|
}
|
|
1369
1503
|
}
|
|
1370
1504
|
const uniqueKeysToRemove = [...new Set(keysToRemove)];
|
|
1371
1505
|
uniqueKeysToRemove.forEach((key) => {
|
|
1372
|
-
|
|
1506
|
+
this.storage.remove(key);
|
|
1373
1507
|
count++;
|
|
1374
1508
|
});
|
|
1375
1509
|
return count;
|