tenneo-auth-plugin 1.0.2 → 1.0.4
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.d.mts +104 -27
- package/dist/index.d.ts +104 -27
- package/dist/index.js +840 -557
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +824 -557
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -373,35 +373,7 @@ function createCookieStorageAdapter(namespace, options) {
|
|
|
373
373
|
};
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
// src/infrastructure/storage/
|
|
377
|
-
var KEY_SUFFIXES = {
|
|
378
|
-
tenantKey: "tenant_key",
|
|
379
|
-
codeVerifier: "code_verifier",
|
|
380
|
-
state: "state",
|
|
381
|
-
accessToken: "access_token",
|
|
382
|
-
refreshToken: "refresh_token",
|
|
383
|
-
expiresAt: "expires_at"
|
|
384
|
-
};
|
|
385
|
-
var STORAGE_KEYS = {
|
|
386
|
-
get tenantKey() {
|
|
387
|
-
return getStorageKey(KEY_SUFFIXES.tenantKey);
|
|
388
|
-
},
|
|
389
|
-
get codeVerifier() {
|
|
390
|
-
return getStorageKey(KEY_SUFFIXES.codeVerifier);
|
|
391
|
-
},
|
|
392
|
-
get state() {
|
|
393
|
-
return getStorageKey(KEY_SUFFIXES.state);
|
|
394
|
-
},
|
|
395
|
-
get accessToken() {
|
|
396
|
-
return getStorageKey(KEY_SUFFIXES.accessToken);
|
|
397
|
-
},
|
|
398
|
-
get refreshToken() {
|
|
399
|
-
return getStorageKey(KEY_SUFFIXES.refreshToken);
|
|
400
|
-
},
|
|
401
|
-
get expiresAt() {
|
|
402
|
-
return getStorageKey(KEY_SUFFIXES.expiresAt);
|
|
403
|
-
}
|
|
404
|
-
};
|
|
376
|
+
// src/infrastructure/storage/kv.ts
|
|
405
377
|
function keyToSuffix(key) {
|
|
406
378
|
const prefix = getStorageKeyPrefix();
|
|
407
379
|
return key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
@@ -437,6 +409,36 @@ function removeStorage(key) {
|
|
|
437
409
|
} catch {
|
|
438
410
|
}
|
|
439
411
|
}
|
|
412
|
+
|
|
413
|
+
// src/infrastructure/storage/api.ts
|
|
414
|
+
var KEY_SUFFIXES = {
|
|
415
|
+
tenantKey: "tenant_key",
|
|
416
|
+
codeVerifier: "code_verifier",
|
|
417
|
+
state: "state",
|
|
418
|
+
accessToken: "access_token",
|
|
419
|
+
refreshToken: "refresh_token",
|
|
420
|
+
expiresAt: "expires_at"
|
|
421
|
+
};
|
|
422
|
+
var STORAGE_KEYS = {
|
|
423
|
+
get tenantKey() {
|
|
424
|
+
return getStorageKey(KEY_SUFFIXES.tenantKey);
|
|
425
|
+
},
|
|
426
|
+
get codeVerifier() {
|
|
427
|
+
return getStorageKey(KEY_SUFFIXES.codeVerifier);
|
|
428
|
+
},
|
|
429
|
+
get state() {
|
|
430
|
+
return getStorageKey(KEY_SUFFIXES.state);
|
|
431
|
+
},
|
|
432
|
+
get accessToken() {
|
|
433
|
+
return getStorageKey(KEY_SUFFIXES.accessToken);
|
|
434
|
+
},
|
|
435
|
+
get refreshToken() {
|
|
436
|
+
return getStorageKey(KEY_SUFFIXES.refreshToken);
|
|
437
|
+
},
|
|
438
|
+
get expiresAt() {
|
|
439
|
+
return getStorageKey(KEY_SUFFIXES.expiresAt);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
440
442
|
function clearAuthStorage() {
|
|
441
443
|
const keysToClear = [
|
|
442
444
|
STORAGE_KEYS.accessToken,
|
|
@@ -499,28 +501,6 @@ function isAccessTokenExpired() {
|
|
|
499
501
|
const buffer = 60 * 1e3;
|
|
500
502
|
return Date.now() >= expiresAt - buffer;
|
|
501
503
|
}
|
|
502
|
-
function readHostConfig() {
|
|
503
|
-
try {
|
|
504
|
-
const raw = getStorage(getStorageKey("host_config"));
|
|
505
|
-
if (!raw) return {};
|
|
506
|
-
const parsed = JSON.parse(raw);
|
|
507
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
508
|
-
} catch {
|
|
509
|
-
return {};
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
function getClientId() {
|
|
513
|
-
const value = readHostConfig().clientId;
|
|
514
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
515
|
-
}
|
|
516
|
-
function getRedirectUri() {
|
|
517
|
-
const value = readHostConfig().redirectUri;
|
|
518
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
519
|
-
}
|
|
520
|
-
function getTenantId() {
|
|
521
|
-
const value = readHostConfig().tenantId;
|
|
522
|
-
return typeof value === "string" && value.trim() ? value : null;
|
|
523
|
-
}
|
|
524
504
|
function getCodeVerifier() {
|
|
525
505
|
return getStorage(STORAGE_KEYS.codeVerifier);
|
|
526
506
|
}
|
|
@@ -581,6 +561,252 @@ var Logger = class {
|
|
|
581
561
|
};
|
|
582
562
|
var logger = new Logger();
|
|
583
563
|
|
|
564
|
+
// src/config/resolver-core.ts
|
|
565
|
+
var inMemoryConfig = {};
|
|
566
|
+
var hostConfigProvider = null;
|
|
567
|
+
var CACHE_MARKER = "__tenneo_host_config_cache";
|
|
568
|
+
var CACHE_AT = "__tenneo_host_config_cache_at";
|
|
569
|
+
function setConfig(overrides) {
|
|
570
|
+
inMemoryConfig = { ...inMemoryConfig, ...overrides };
|
|
571
|
+
}
|
|
572
|
+
function clearConfigOverrides() {
|
|
573
|
+
inMemoryConfig = {};
|
|
574
|
+
}
|
|
575
|
+
function setHostConfigProvider(fn) {
|
|
576
|
+
hostConfigProvider = fn;
|
|
577
|
+
}
|
|
578
|
+
function readHostBridge() {
|
|
579
|
+
const out = {};
|
|
580
|
+
try {
|
|
581
|
+
if (typeof window !== "undefined" && window.__TENNEO_AUTH_HOST__) {
|
|
582
|
+
Object.assign(out, window.__TENNEO_AUTH_HOST__);
|
|
583
|
+
}
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
const fromProvider = hostConfigProvider?.();
|
|
588
|
+
if (fromProvider && typeof fromProvider === "object") {
|
|
589
|
+
Object.assign(out, fromProvider);
|
|
590
|
+
}
|
|
591
|
+
} catch (e) {
|
|
592
|
+
logger.warn("[config] hostConfigProvider threw", {
|
|
593
|
+
error: e instanceof Error ? e.message : String(e)
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return out;
|
|
597
|
+
}
|
|
598
|
+
function readStorageConfigCache() {
|
|
599
|
+
try {
|
|
600
|
+
const raw = getStorage(getStorageKey("host_config"));
|
|
601
|
+
if (!raw) return {};
|
|
602
|
+
const parsed = JSON.parse(raw);
|
|
603
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
604
|
+
const { [CACHE_MARKER]: _m, [CACHE_AT]: _t, ...rest } = parsed;
|
|
605
|
+
if (!rest.clientId && typeof rest.client_id === "string" && rest.client_id.trim()) {
|
|
606
|
+
rest.clientId = rest.client_id.trim();
|
|
607
|
+
}
|
|
608
|
+
return rest;
|
|
609
|
+
} catch {
|
|
610
|
+
return {};
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function resolveSDKConfig(overrides) {
|
|
614
|
+
const stored = readStorageConfigCache();
|
|
615
|
+
const bridge = readHostBridge();
|
|
616
|
+
return {
|
|
617
|
+
...stored,
|
|
618
|
+
...bridge,
|
|
619
|
+
...inMemoryConfig,
|
|
620
|
+
...overrides ?? {}
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function nonEmptyString(v) {
|
|
624
|
+
if (v == null) return void 0;
|
|
625
|
+
const s = String(v).trim();
|
|
626
|
+
return s || void 0;
|
|
627
|
+
}
|
|
628
|
+
function getConfigFieldSources(overrides) {
|
|
629
|
+
const stored = readStorageConfigCache();
|
|
630
|
+
const bridge = readHostBridge();
|
|
631
|
+
const layers = [
|
|
632
|
+
{ src: "storage_cache", cfg: stored },
|
|
633
|
+
{ src: "host_bridge", cfg: bridge },
|
|
634
|
+
{ src: "memory", cfg: inMemoryConfig }
|
|
635
|
+
];
|
|
636
|
+
if (overrides && Object.keys(overrides).length) {
|
|
637
|
+
layers.push({ src: "override", cfg: overrides });
|
|
638
|
+
}
|
|
639
|
+
const pick = (key) => {
|
|
640
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
641
|
+
const v = nonEmptyString(layers[i].cfg[key]);
|
|
642
|
+
if (v !== void 0) return layers[i].src;
|
|
643
|
+
}
|
|
644
|
+
return void 0;
|
|
645
|
+
};
|
|
646
|
+
return {
|
|
647
|
+
tenantCode: pick("tenantCode") ?? pick("tenantKey"),
|
|
648
|
+
tenantKey: pick("tenantKey") ?? pick("tenantCode"),
|
|
649
|
+
apiBaseUrl: pick("apiBaseUrl"),
|
|
650
|
+
authServerUrl: pick("authServerUrl"),
|
|
651
|
+
callbackUrl: pick("callbackUrl"),
|
|
652
|
+
clientCode: pick("clientCode"),
|
|
653
|
+
clientId: pick("clientId"),
|
|
654
|
+
tenantId: pick("tenantId"),
|
|
655
|
+
appId: pick("appId")
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function getEffectiveTenantCode(overrides) {
|
|
659
|
+
const cfg = resolveSDKConfig(overrides);
|
|
660
|
+
const t = nonEmptyString(cfg.tenantCode) ?? nonEmptyString(cfg.tenantKey) ?? nonEmptyString(cfg.clientCode) ?? "";
|
|
661
|
+
return t;
|
|
662
|
+
}
|
|
663
|
+
var getEffectiveTenantKey = getEffectiveTenantCode;
|
|
664
|
+
function trim(v) {
|
|
665
|
+
return v == null ? "" : String(v).trim();
|
|
666
|
+
}
|
|
667
|
+
function toResolvedConfig(cfg) {
|
|
668
|
+
const merged = { ...cfg };
|
|
669
|
+
if (!merged.tenantCode && merged.tenantKey) {
|
|
670
|
+
merged.tenantCode = merged.tenantKey;
|
|
671
|
+
}
|
|
672
|
+
const clientId = trim(merged.clientId) || trim(merged.clientCode);
|
|
673
|
+
return {
|
|
674
|
+
apiBaseUrl: trim(merged.apiBaseUrl),
|
|
675
|
+
authServerUrl: trim(merged.authServerUrl),
|
|
676
|
+
callbackUrl: trim(merged.callbackUrl) || trim(merged.redirectUri),
|
|
677
|
+
clientCode: trim(merged.clientCode),
|
|
678
|
+
tenantCode: trim(merged.tenantCode),
|
|
679
|
+
appId: trim(merged.appId),
|
|
680
|
+
clientId,
|
|
681
|
+
tenantId: trim(merged.tenantId)
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
var AuthConfigError = class extends Error {
|
|
685
|
+
constructor(missing, message) {
|
|
686
|
+
super(
|
|
687
|
+
message ?? `[tenneo-auth] Missing required auth configuration: ${missing.join(", ")}. Provide authServerUrl, callbackUrl (redirectUri), and tenant_key/tenant_code via AuthProvider/initPlugin or window.__TENNEO_AUTH_HOST__.`
|
|
688
|
+
);
|
|
689
|
+
this.code = "AUTH_CONFIG_MISSING";
|
|
690
|
+
this.name = "AuthConfigError";
|
|
691
|
+
this.missing = missing;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
function resolveConfig(overrides) {
|
|
695
|
+
return toResolvedConfig(resolveSDKConfig(overrides));
|
|
696
|
+
}
|
|
697
|
+
function getAuthConfig(options) {
|
|
698
|
+
const resolved = toResolvedConfig(resolveSDKConfig(options?.overrides));
|
|
699
|
+
if (options?.strict) {
|
|
700
|
+
const missing = [];
|
|
701
|
+
if (!resolved.authServerUrl) missing.push("authServerUrl");
|
|
702
|
+
if (!resolved.callbackUrl) missing.push("callbackUrl");
|
|
703
|
+
if (!resolved.tenantCode) missing.push("tenantCode|tenantKey");
|
|
704
|
+
if (missing.length) throw new AuthConfigError(missing);
|
|
705
|
+
}
|
|
706
|
+
return resolved;
|
|
707
|
+
}
|
|
708
|
+
function syncHostConfigCacheFromMemory() {
|
|
709
|
+
const c = resolveSDKConfig();
|
|
710
|
+
const patch = {};
|
|
711
|
+
const keys = [
|
|
712
|
+
"apiBaseUrl",
|
|
713
|
+
"authServerUrl",
|
|
714
|
+
"callbackUrl",
|
|
715
|
+
"redirectUri",
|
|
716
|
+
"tenantCode",
|
|
717
|
+
"tenantKey",
|
|
718
|
+
"clientCode",
|
|
719
|
+
"clientId",
|
|
720
|
+
"tenantId",
|
|
721
|
+
"appId",
|
|
722
|
+
"basePath"
|
|
723
|
+
];
|
|
724
|
+
for (const k of keys) {
|
|
725
|
+
const v = c[k];
|
|
726
|
+
if (v != null && String(v).trim()) {
|
|
727
|
+
patch[k] = String(v).trim();
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (Object.keys(patch).length) writeHostConfigCache(patch);
|
|
731
|
+
}
|
|
732
|
+
function writeHostConfigCache(partial) {
|
|
733
|
+
try {
|
|
734
|
+
const key = getStorageKey("host_config");
|
|
735
|
+
const raw = getStorage(key);
|
|
736
|
+
let existing = {};
|
|
737
|
+
try {
|
|
738
|
+
if (raw) existing = JSON.parse(raw);
|
|
739
|
+
} catch {
|
|
740
|
+
existing = {};
|
|
741
|
+
}
|
|
742
|
+
const next = {
|
|
743
|
+
...existing,
|
|
744
|
+
...partial,
|
|
745
|
+
[CACHE_MARKER]: true,
|
|
746
|
+
[CACHE_AT]: (/* @__PURE__ */ new Date()).toISOString()
|
|
747
|
+
};
|
|
748
|
+
setStorage(key, JSON.stringify(next));
|
|
749
|
+
logger.debug("[config] host_config cache updated (non-authoritative)", {
|
|
750
|
+
keys: Object.keys(partial)
|
|
751
|
+
});
|
|
752
|
+
} catch (e) {
|
|
753
|
+
logger.warn("[config] Failed to write host_config cache", {
|
|
754
|
+
error: e instanceof Error ? e.message : String(e)
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function initPlugin(config, opts) {
|
|
759
|
+
setConfig(config);
|
|
760
|
+
logger.info("[config] initPlugin \u2014 runtime config updated", {
|
|
761
|
+
syncStorageCache: opts?.syncStorageCache !== false,
|
|
762
|
+
tenantCode: config.tenantCode ?? config.tenantKey ?? "(none)"
|
|
763
|
+
});
|
|
764
|
+
if (opts?.syncStorageCache !== false) {
|
|
765
|
+
writeHostConfigCache(config);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function logConfigResolutionDebug(context, overrides) {
|
|
769
|
+
const sources = getConfigFieldSources(overrides);
|
|
770
|
+
logger.debug(`[config] ${context}`, {
|
|
771
|
+
fieldSources: sources,
|
|
772
|
+
effectiveTenantCode: getEffectiveTenantCode(overrides) || "(empty)",
|
|
773
|
+
hasAuthServerUrl: !!resolveSDKConfig(overrides).authServerUrl,
|
|
774
|
+
hasCallbackUrl: !!resolveSDKConfig(overrides).callbackUrl
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/config/host-fields.ts
|
|
779
|
+
function trim2(v) {
|
|
780
|
+
if (v == null) return null;
|
|
781
|
+
const s = String(v).trim();
|
|
782
|
+
return s || null;
|
|
783
|
+
}
|
|
784
|
+
function oauthClientIdForTokenRequest(raw) {
|
|
785
|
+
if (!raw) return null;
|
|
786
|
+
let s = raw.trim();
|
|
787
|
+
if (!s) return null;
|
|
788
|
+
if (s.toLowerCase().startsWith("public-")) {
|
|
789
|
+
s = s.slice("public-".length).trim();
|
|
790
|
+
}
|
|
791
|
+
return s || null;
|
|
792
|
+
}
|
|
793
|
+
function getClientId() {
|
|
794
|
+
const c = resolveSDKConfig();
|
|
795
|
+
const fromId = oauthClientIdForTokenRequest(trim2(c.clientId));
|
|
796
|
+
if (fromId) return fromId;
|
|
797
|
+
return oauthClientIdForTokenRequest(trim2(c.clientCode));
|
|
798
|
+
}
|
|
799
|
+
function normalizeClientIdForOAuthTokenBody(raw) {
|
|
800
|
+
return oauthClientIdForTokenRequest(trim2(raw));
|
|
801
|
+
}
|
|
802
|
+
function getRedirectUri() {
|
|
803
|
+
const c = resolveSDKConfig();
|
|
804
|
+
return trim2(c.callbackUrl) ?? trim2(c.redirectUri);
|
|
805
|
+
}
|
|
806
|
+
function getTenantId() {
|
|
807
|
+
return trim2(resolveSDKConfig().tenantId);
|
|
808
|
+
}
|
|
809
|
+
|
|
584
810
|
// src/domain/errors.ts
|
|
585
811
|
function isLocalhostHostname(hostname) {
|
|
586
812
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.startsWith("172.16.");
|
|
@@ -779,71 +1005,47 @@ var DEFAULT_TOKEN_GRANT_TYPE = OAUTH_CONSTANTS.TOKEN_GRANT_TYPE;
|
|
|
779
1005
|
var DEFAULT_REFRESH_GRANT_TYPE = OAUTH_CONSTANTS.REFRESH_GRANT_TYPE;
|
|
780
1006
|
|
|
781
1007
|
// src/config/index.ts
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
try {
|
|
785
|
-
const raw = getStorage(getStorageKey("host_config"));
|
|
786
|
-
if (!raw) return {};
|
|
787
|
-
const parsed = JSON.parse(raw);
|
|
788
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
789
|
-
} catch {
|
|
790
|
-
return {};
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
function resolveMerged(overrides) {
|
|
794
|
-
const stored = readHostConfigFromStorage();
|
|
795
|
-
const cfg = { ...stored, ...inMemoryConfig, ...overrides ?? {} };
|
|
796
|
-
if (!cfg.tenantCode && cfg.tenantKey) {
|
|
797
|
-
cfg.tenantCode = cfg.tenantKey;
|
|
798
|
-
}
|
|
799
|
-
const trimmed = (v) => v == null ? "" : String(v).trim();
|
|
800
|
-
return {
|
|
801
|
-
apiBaseUrl: trimmed(cfg.apiBaseUrl),
|
|
802
|
-
authServerUrl: trimmed(cfg.authServerUrl),
|
|
803
|
-
callbackUrl: trimmed(cfg.callbackUrl),
|
|
804
|
-
clientCode: trimmed(cfg.clientCode),
|
|
805
|
-
tenantCode: trimmed(cfg.tenantCode),
|
|
806
|
-
appId: trimmed(cfg.appId)
|
|
807
|
-
};
|
|
1008
|
+
function getAuthConfig2(options) {
|
|
1009
|
+
return getAuthConfig(options);
|
|
808
1010
|
}
|
|
809
|
-
function
|
|
810
|
-
return
|
|
1011
|
+
function requireAuthConfig(overrides) {
|
|
1012
|
+
return getAuthConfig({ strict: true, overrides });
|
|
811
1013
|
}
|
|
812
1014
|
function getConfig() {
|
|
813
|
-
return
|
|
814
|
-
}
|
|
815
|
-
function getAuthConfig() {
|
|
816
|
-
return resolveMerged();
|
|
817
|
-
}
|
|
818
|
-
function setConfig(overrides) {
|
|
819
|
-
inMemoryConfig = { ...inMemoryConfig, ...overrides };
|
|
820
|
-
}
|
|
821
|
-
function clearConfigOverrides() {
|
|
822
|
-
inMemoryConfig = {};
|
|
1015
|
+
return resolveConfig();
|
|
823
1016
|
}
|
|
824
1017
|
function getRedirectUri2() {
|
|
825
|
-
const cfg =
|
|
1018
|
+
const cfg = resolveConfig();
|
|
826
1019
|
if (cfg.callbackUrl) return cfg.callbackUrl;
|
|
827
1020
|
if (typeof window !== "undefined") return `${window.location.origin}/callback`;
|
|
828
1021
|
return "";
|
|
829
1022
|
}
|
|
830
1023
|
var Config = {
|
|
831
1024
|
getApiBaseUrl() {
|
|
832
|
-
return
|
|
1025
|
+
return resolveConfig().apiBaseUrl;
|
|
833
1026
|
},
|
|
834
1027
|
getAuthServerUrl() {
|
|
835
|
-
return
|
|
1028
|
+
return resolveConfig().authServerUrl;
|
|
836
1029
|
},
|
|
837
1030
|
getClientCode() {
|
|
838
|
-
return
|
|
1031
|
+
return resolveConfig().clientCode;
|
|
839
1032
|
},
|
|
840
1033
|
getRedirectUri() {
|
|
841
1034
|
return getRedirectUri2();
|
|
842
1035
|
},
|
|
843
1036
|
getAll() {
|
|
844
|
-
return
|
|
1037
|
+
return resolveConfig();
|
|
845
1038
|
}
|
|
846
1039
|
};
|
|
1040
|
+
function applyRuntimeTenantSwitch(tenantKey, opts) {
|
|
1041
|
+
const t = tenantKey.trim();
|
|
1042
|
+
if (!t) return;
|
|
1043
|
+
setConfig({ tenantCode: t, tenantKey: t });
|
|
1044
|
+
logConfigResolutionDebug("applyRuntimeTenantSwitch", { tenantCode: t, tenantKey: t });
|
|
1045
|
+
if (opts?.syncStorageCache !== false) {
|
|
1046
|
+
writeHostConfigCache({ tenantCode: t, tenantKey: t });
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
847
1049
|
|
|
848
1050
|
// src/infrastructure/tenant/TenantConfigCache.ts
|
|
849
1051
|
var DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -919,16 +1121,6 @@ function consumePropsConfig() {
|
|
|
919
1121
|
removeStorage(propsConfigKey);
|
|
920
1122
|
}
|
|
921
1123
|
}
|
|
922
|
-
function readHostConfig2() {
|
|
923
|
-
try {
|
|
924
|
-
const raw = getStorage(getStorageKey("host_config"));
|
|
925
|
-
if (!raw) return null;
|
|
926
|
-
const parsed = JSON.parse(raw);
|
|
927
|
-
return parsed && typeof parsed === "object" ? parsed : null;
|
|
928
|
-
} catch {
|
|
929
|
-
return null;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
1124
|
function validateTenantKey(value) {
|
|
933
1125
|
if (typeof value !== "string") return null;
|
|
934
1126
|
const trimmed = value.trim();
|
|
@@ -937,17 +1129,16 @@ function validateTenantKey(value) {
|
|
|
937
1129
|
function resolveTenantKey() {
|
|
938
1130
|
const propsConfig = consumePropsConfig();
|
|
939
1131
|
const tenantFromProps = validateTenantKey(propsConfig?.tenantKey);
|
|
940
|
-
if (tenantFromProps)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
logger.warn("Missing host_config while resolving tenant");
|
|
944
|
-
return null;
|
|
1132
|
+
if (tenantFromProps) {
|
|
1133
|
+
logger.debug("[tenant] Resolved tenant from props_config", { tenantKey: tenantFromProps });
|
|
1134
|
+
return tenantFromProps;
|
|
945
1135
|
}
|
|
946
|
-
const
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
|
|
1136
|
+
const fromHost = validateTenantKey(getEffectiveTenantCode());
|
|
1137
|
+
if (fromHost) {
|
|
1138
|
+
logger.debug("[tenant] Resolved tenant from host/runtime config", { tenantKey: fromHost });
|
|
1139
|
+
return fromHost;
|
|
950
1140
|
}
|
|
1141
|
+
logger.warn("[tenant] Unable to resolve tenant key (props + host/runtime empty)");
|
|
951
1142
|
return null;
|
|
952
1143
|
}
|
|
953
1144
|
async function resolveTenantConfig(tenantKey, tenantResolver) {
|
|
@@ -1080,7 +1271,7 @@ async function refreshAccessToken() {
|
|
|
1080
1271
|
});
|
|
1081
1272
|
throw new Error("Cannot refresh token: missing required data");
|
|
1082
1273
|
}
|
|
1083
|
-
const apiAuthBaseUrl = Config.
|
|
1274
|
+
const apiAuthBaseUrl = Config.getAuthServerUrl();
|
|
1084
1275
|
const tokenUrl = `${apiAuthBaseUrl}/oauth/refresh`;
|
|
1085
1276
|
const body = new URLSearchParams({
|
|
1086
1277
|
grant_type: DEFAULT_REFRESH_GRANT_TYPE,
|
|
@@ -1139,6 +1330,56 @@ async function refreshAccessToken() {
|
|
|
1139
1330
|
return refreshPromise;
|
|
1140
1331
|
}
|
|
1141
1332
|
|
|
1333
|
+
// src/strategies/oauth2-pkce/pkce.ts
|
|
1334
|
+
function generateCodeVerifier(length = 128) {
|
|
1335
|
+
const array = new Uint8Array(length);
|
|
1336
|
+
crypto.getRandomValues(array);
|
|
1337
|
+
const base64 = btoa(String.fromCharCode(...array));
|
|
1338
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1339
|
+
}
|
|
1340
|
+
async function generateCodeChallenge(verifier) {
|
|
1341
|
+
try {
|
|
1342
|
+
const encoder = new TextEncoder();
|
|
1343
|
+
const data = encoder.encode(verifier);
|
|
1344
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
1345
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1346
|
+
const base64 = btoa(String.fromCharCode(...hashArray));
|
|
1347
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1348
|
+
} catch {
|
|
1349
|
+
throw new Error("Failed to generate code challenge");
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
function generateState() {
|
|
1353
|
+
const array = new Uint8Array(32);
|
|
1354
|
+
crypto.getRandomValues(array);
|
|
1355
|
+
const base64 = btoa(String.fromCharCode(...array));
|
|
1356
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/utils/validation.ts
|
|
1360
|
+
function isValidGuid(guid) {
|
|
1361
|
+
if (!guid || typeof guid !== "string") {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1365
|
+
return guidRegex.test(guid.trim());
|
|
1366
|
+
}
|
|
1367
|
+
function validateTenantId(tenantId) {
|
|
1368
|
+
if (!tenantId || tenantId.trim() === "") {
|
|
1369
|
+
return {
|
|
1370
|
+
isValid: false,
|
|
1371
|
+
error: "Tenant ID is required"
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
if (!isValidGuid(tenantId)) {
|
|
1375
|
+
return {
|
|
1376
|
+
isValid: false,
|
|
1377
|
+
error: "Tenant ID must be a valid GUID format (00000000-0000-0000-0000-000000000001)"
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
return { isValid: true };
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1142
1383
|
// src/application/security/redirectLoopGuard.ts
|
|
1143
1384
|
var DEFAULT_MAX_REDIRECTS = 5;
|
|
1144
1385
|
var WINDOW_MS = 60 * 1e3;
|
|
@@ -1163,18 +1404,166 @@ function checkAndIncrementRedirect(maxAttempts = DEFAULT_MAX_REDIRECTS) {
|
|
|
1163
1404
|
redirectCount++;
|
|
1164
1405
|
return true;
|
|
1165
1406
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1407
|
+
|
|
1408
|
+
// src/strategies/oauth2-pkce/oauth.ts
|
|
1409
|
+
function storeTenantConfig(config) {
|
|
1410
|
+
const validation = validateTenantId(config.tenantId);
|
|
1411
|
+
if (!validation.isValid) {
|
|
1412
|
+
logger.warn("Tenant ID validation failed", { tenantId: config.tenantId });
|
|
1413
|
+
}
|
|
1414
|
+
const authServerToStore = Config.getAuthServerUrl() || config.authServer;
|
|
1415
|
+
const redirectUriToStore = Config.getRedirectUri();
|
|
1416
|
+
writeHostConfigCache({
|
|
1417
|
+
authServerUrl: authServerToStore,
|
|
1418
|
+
clientId: config.client.clientId,
|
|
1419
|
+
clientCode: config.client.clientCode ?? config.client.clientId,
|
|
1420
|
+
redirectUri: redirectUriToStore,
|
|
1421
|
+
callbackUrl: redirectUriToStore,
|
|
1422
|
+
tenantId: config.tenantId,
|
|
1423
|
+
tenantKey: config.tenantKey ?? "",
|
|
1424
|
+
tenantCode: config.tenantKey ?? ""
|
|
1425
|
+
});
|
|
1426
|
+
if (config.tenantKey) {
|
|
1427
|
+
setStorage(STORAGE_KEYS.tenantKey, config.tenantKey);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
async function initiateAuthFlow(config) {
|
|
1431
|
+
try {
|
|
1432
|
+
logger.info("Initiating OAuth authorization flow");
|
|
1433
|
+
const authServerFromProps = Config.getAuthServerUrl();
|
|
1434
|
+
const authServerEffective = (authServerFromProps || config?.authServer || "").replace(/\/+$/, "");
|
|
1435
|
+
const redirectUriEffective = Config.getRedirectUri();
|
|
1436
|
+
const tenantIdEffective = config?.tenantId || "";
|
|
1437
|
+
const clientIdEffective = config?.client?.clientId || "";
|
|
1438
|
+
logger.debug("[initiateAuthFlow] Resolved inputs", {
|
|
1439
|
+
authServerFromProps: authServerFromProps ? "(provided)" : "(empty)",
|
|
1440
|
+
authServerEffective,
|
|
1441
|
+
redirectUriEffective,
|
|
1442
|
+
tenantIdEffective,
|
|
1443
|
+
clientIdEffective,
|
|
1444
|
+
scopes: config?.client?.scopes,
|
|
1445
|
+
hasRuntime: !!getAuthRuntime()
|
|
1446
|
+
});
|
|
1447
|
+
logger.info("[initiateAuthFlow] authServerUrl for OAuth", {
|
|
1448
|
+
authServerFromProps: authServerFromProps || null,
|
|
1449
|
+
authServerEffective,
|
|
1450
|
+
redirectUriEffective,
|
|
1451
|
+
tenantId: tenantIdEffective,
|
|
1452
|
+
tenantKey: config.tenantKey ?? null,
|
|
1453
|
+
clientIdEffective
|
|
1454
|
+
});
|
|
1455
|
+
const token = getAccessToken();
|
|
1456
|
+
if (token && !isAccessTokenExpired()) {
|
|
1457
|
+
logger.debug("Valid token already exists, skipping auth flow");
|
|
1458
|
+
clearFlowFlags();
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (isCallbackUrl()) {
|
|
1462
|
+
logger.debug("Callback URL detected - clearing flow flag to allow callback processing");
|
|
1463
|
+
clearFlowFlags();
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (isFlowInProgress()) {
|
|
1467
|
+
if (isFlowFlagStale(3e3)) {
|
|
1468
|
+
logger.info("Stale auth flow flag detected, clearing and proceeding");
|
|
1469
|
+
clearFlowFlags();
|
|
1470
|
+
} else {
|
|
1471
|
+
logger.debug("Auth flow already in progress (recent), skipping duplicate redirect");
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
setFlowFlags();
|
|
1476
|
+
if (!(Config.getAuthServerUrl() || config?.authServer) || !config.client?.clientId || !config.client?.redirectUri) {
|
|
1477
|
+
logger.error("[initiateAuthFlow] Missing required tenant config", {
|
|
1478
|
+
authServerEffective,
|
|
1479
|
+
clientId: config?.client?.clientId,
|
|
1480
|
+
redirectUriFromTenantApi: config?.client?.redirectUri,
|
|
1481
|
+
redirectUriEffective
|
|
1482
|
+
});
|
|
1483
|
+
throw new Error("Invalid tenant configuration for auth flow");
|
|
1484
|
+
}
|
|
1485
|
+
storeTenantConfig(config);
|
|
1486
|
+
logger.debug("[initiateAuthFlow] Stored tenant config, generating PKCE");
|
|
1487
|
+
removeStorage(getStorageKey("callback_processed"));
|
|
1488
|
+
removeStorage(getStorageKey("state_mismatch"));
|
|
1489
|
+
clearCallbackProcessing();
|
|
1490
|
+
const codeVerifier = generateCodeVerifier();
|
|
1491
|
+
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
1492
|
+
const state = generateState();
|
|
1493
|
+
removeStorage(STORAGE_KEYS.codeVerifier);
|
|
1494
|
+
removeStorage(STORAGE_KEYS.state);
|
|
1495
|
+
setStorage(STORAGE_KEYS.codeVerifier, codeVerifier);
|
|
1496
|
+
setStorage(STORAGE_KEYS.state, state);
|
|
1497
|
+
const authUrl = buildAuthorizationUrl(config, codeChallenge, state);
|
|
1498
|
+
logger.debug("[initiateAuthFlow] Built authorization URL", {
|
|
1499
|
+
authServerEffective,
|
|
1500
|
+
urlPrefix: authUrl?.slice(0, 80)
|
|
1501
|
+
});
|
|
1502
|
+
if (!authUrl || !(authUrl.startsWith("http://") || authUrl.startsWith("https://"))) {
|
|
1503
|
+
throw new Error("Invalid authorization URL generated");
|
|
1504
|
+
}
|
|
1505
|
+
if (!checkAndIncrementRedirect()) {
|
|
1506
|
+
logger.warn("Redirect loop protection: max redirect attempts exceeded");
|
|
1507
|
+
clearFlowFlags();
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
logger.info("Redirecting to authorization server");
|
|
1511
|
+
emitAuthEvent(AuthEventNames.LOGIN_STARTED, { tenantKey: config.tenantKey });
|
|
1512
|
+
const runtime = getAuthRuntime();
|
|
1513
|
+
if (runtime) {
|
|
1514
|
+
logger.debug("[initiateAuthFlow] Redirect via runtime.urlProvider.redirect");
|
|
1515
|
+
runtime.urlProvider.redirect(authUrl);
|
|
1516
|
+
} else if (typeof window !== "undefined") {
|
|
1517
|
+
logger.debug("[initiateAuthFlow] Redirect via window.location.replace");
|
|
1518
|
+
window.location.replace(authUrl);
|
|
1519
|
+
} else {
|
|
1520
|
+
logger.error("[initiateAuthFlow] No runtime and no window; cannot redirect", { authUrl });
|
|
1521
|
+
}
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
clearFlowFlags();
|
|
1524
|
+
logger.error("Failed to initiate auth flow", {
|
|
1525
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1526
|
+
});
|
|
1527
|
+
throw error;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
function buildAuthorizationUrl(config, codeChallenge, state) {
|
|
1531
|
+
const authServerBase = (Config.getAuthServerUrl() || config.authServer || "").replace(/\/+$/, "");
|
|
1532
|
+
if (isLocalhost2()) {
|
|
1533
|
+
logger.debug("Building authorization URL", {
|
|
1534
|
+
authServer: authServerBase,
|
|
1535
|
+
clientId: config.client.clientId,
|
|
1536
|
+
redirectUri: config.client.redirectUri,
|
|
1537
|
+
scopes: config.client.scopes
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
const tenantIdToUse = config.tenantId;
|
|
1541
|
+
const clientIdToUse = config.client.clientId;
|
|
1542
|
+
const redirectUriToUse = Config.getRedirectUri();
|
|
1543
|
+
const params = new URLSearchParams({
|
|
1544
|
+
client_id: `public-${clientIdToUse}`,
|
|
1545
|
+
tenant_id: tenantIdToUse,
|
|
1546
|
+
redirect_uri: redirectUriToUse,
|
|
1547
|
+
response_type: DEFAULT_RESPONSE_TYPE,
|
|
1548
|
+
scope: config.client.scopes,
|
|
1549
|
+
state,
|
|
1550
|
+
code_challenge: codeChallenge,
|
|
1551
|
+
code_challenge_method: DEFAULT_CODE_CHALLENGE_METHOD
|
|
1552
|
+
});
|
|
1553
|
+
return `${authServerBase}/connect/authorize?${params.toString()}`;
|
|
1554
|
+
}
|
|
1555
|
+
function isProcessed() {
|
|
1556
|
+
return getStorage(getStorageKey("callback_processed")) === "true";
|
|
1557
|
+
}
|
|
1558
|
+
function isProcessing() {
|
|
1559
|
+
return isCallbackProcessing();
|
|
1560
|
+
}
|
|
1561
|
+
function markProcessing() {
|
|
1562
|
+
setCallbackProcessing();
|
|
1563
|
+
}
|
|
1564
|
+
function markProcessed() {
|
|
1565
|
+
setStorage(getStorageKey("callback_processed"), "true");
|
|
1566
|
+
clearCallbackProcessing();
|
|
1178
1567
|
}
|
|
1179
1568
|
function clearProcessing() {
|
|
1180
1569
|
clearCallbackProcessing();
|
|
@@ -1193,6 +1582,38 @@ function cleanUrlSoft() {
|
|
|
1193
1582
|
function clearAllAuthFlags() {
|
|
1194
1583
|
clearFlowFlags();
|
|
1195
1584
|
}
|
|
1585
|
+
async function tryRecoverOAuthConfigFromTenant() {
|
|
1586
|
+
const runtime = getAuthRuntime();
|
|
1587
|
+
const tenantKey = (resolveTenantKey() ?? getTenantKey() ?? "").trim();
|
|
1588
|
+
if (!runtime?.tenantResolver || !tenantKey) {
|
|
1589
|
+
logger.debug("[Callback] OAuth config recovery skipped \u2014 no tenantResolver or tenantKey", {
|
|
1590
|
+
hasResolver: !!runtime?.tenantResolver,
|
|
1591
|
+
tenantKey: tenantKey || "(empty)"
|
|
1592
|
+
});
|
|
1593
|
+
return null;
|
|
1594
|
+
}
|
|
1595
|
+
try {
|
|
1596
|
+
const tc = await runtime.tenantResolver.resolve(tenantKey);
|
|
1597
|
+
if (!tc?.client?.clientId || !tc.tenantId) {
|
|
1598
|
+
logger.warn("[Callback] OAuth config recovery: tenant resolve missing client or tenantId", {
|
|
1599
|
+
tenantKey
|
|
1600
|
+
});
|
|
1601
|
+
return null;
|
|
1602
|
+
}
|
|
1603
|
+
storeTenantConfig(tc);
|
|
1604
|
+
return {
|
|
1605
|
+
clientId: tc.client.clientId.trim() || null,
|
|
1606
|
+
redirectUri: tc.client.redirectUri?.trim() || getRedirectUri(),
|
|
1607
|
+
tenantId: tc.tenantId.trim() || null
|
|
1608
|
+
};
|
|
1609
|
+
} catch (e) {
|
|
1610
|
+
logger.warn("[Callback] OAuth config recovery: tenant re-resolve failed", {
|
|
1611
|
+
tenantKey,
|
|
1612
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1613
|
+
});
|
|
1614
|
+
return null;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1196
1617
|
async function handleCallback() {
|
|
1197
1618
|
const runtimeSearch = getAuthRuntime()?.urlProvider.getCurrentUrl().search;
|
|
1198
1619
|
const windowSearch = typeof window !== "undefined" ? window.location.search : "";
|
|
@@ -1275,9 +1696,22 @@ async function handleCallback() {
|
|
|
1275
1696
|
cleanUrlSoft();
|
|
1276
1697
|
return false;
|
|
1277
1698
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1699
|
+
let clientId = getClientId();
|
|
1700
|
+
let redirectUri = getRedirectUri();
|
|
1701
|
+
let tenantId = getTenantId();
|
|
1702
|
+
if (!clientId || !redirectUri || !tenantId) {
|
|
1703
|
+
logger.warn("[Callback] OAuth config incomplete \u2014 attempting tenant re-resolve", {
|
|
1704
|
+
hasClientId: !!clientId,
|
|
1705
|
+
hasRedirectUri: !!redirectUri,
|
|
1706
|
+
hasTenantId: !!tenantId
|
|
1707
|
+
});
|
|
1708
|
+
const recovered = await tryRecoverOAuthConfigFromTenant();
|
|
1709
|
+
if (recovered) {
|
|
1710
|
+
if (!redirectUri) redirectUri = recovered.redirectUri;
|
|
1711
|
+
if (!tenantId) tenantId = recovered.tenantId;
|
|
1712
|
+
clientId = getClientId() ?? normalizeClientIdForOAuthTokenBody(recovered.clientId) ?? null;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1281
1715
|
if (!clientId || !redirectUri || !tenantId) {
|
|
1282
1716
|
logger.error("[Callback] Early exit: missing OAuth config during callback", {
|
|
1283
1717
|
hasClientId: !!clientId,
|
|
@@ -1335,320 +1769,120 @@ async function handleCallback() {
|
|
|
1335
1769
|
if (status === 400 && errorCode === "invalid_grant" && (String(errorDescription).includes("already used") || String(errorDescription).includes("Code not found") || String(errorDescription).includes("expired, already used"))) {
|
|
1336
1770
|
const existingToken2 = getAccessToken();
|
|
1337
1771
|
const tokenValid2 = existingToken2 && !isAccessTokenExpired();
|
|
1338
|
-
if (tokenValid2) {
|
|
1339
|
-
removeStorage(STORAGE_KEYS.codeVerifier);
|
|
1340
|
-
removeStorage(STORAGE_KEYS.state);
|
|
1341
|
-
clearAllAuthFlags();
|
|
1342
|
-
markProcessed();
|
|
1343
|
-
cleanUrlSoft();
|
|
1344
|
-
logger.info("OAuth callback processed successfully (code already used, but token exists)");
|
|
1345
|
-
resetRedirectLoopGuard();
|
|
1346
|
-
emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
|
|
1347
|
-
return true;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
|
|
1351
|
-
error: err.message,
|
|
1352
|
-
message: responseData?.error_description ?? err.message
|
|
1353
|
-
});
|
|
1354
|
-
logger.error("OAuth callback failed (Axios error)", {
|
|
1355
|
-
status,
|
|
1356
|
-
statusText: err.response?.statusText,
|
|
1357
|
-
responseData: err.response?.data,
|
|
1358
|
-
message: err.message,
|
|
1359
|
-
url: err.config?.url
|
|
1360
|
-
});
|
|
1361
|
-
} else {
|
|
1362
|
-
logger.error("OAuth callback failed", {
|
|
1363
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
clearAllAuthFlags();
|
|
1367
|
-
const existingToken = getAccessToken();
|
|
1368
|
-
const tokenValid = existingToken && !isAccessTokenExpired();
|
|
1369
|
-
if (tokenValid) {
|
|
1370
|
-
markProcessed();
|
|
1371
|
-
cleanUrlSoft();
|
|
1372
|
-
resetRedirectLoopGuard();
|
|
1373
|
-
emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
|
|
1374
|
-
return true;
|
|
1375
|
-
}
|
|
1376
|
-
emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
|
|
1377
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1378
|
-
});
|
|
1379
|
-
clearProcessing();
|
|
1380
|
-
if (isCallbackUrl()) cleanUrlSoft();
|
|
1381
|
-
return false;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
// src/application/flows/CallbackFlow.ts
|
|
1386
|
-
var CallbackFlow = class {
|
|
1387
|
-
async execute(context) {
|
|
1388
|
-
const { runtime } = context;
|
|
1389
|
-
const { urlProvider, logger: log } = runtime;
|
|
1390
|
-
const getUrl = () => urlProvider.getCurrentUrl();
|
|
1391
|
-
clearFlowFlags();
|
|
1392
|
-
const handled = await handleCallback();
|
|
1393
|
-
if (handled) {
|
|
1394
|
-
if (isCallbackUrlFromUrl(getUrl())) {
|
|
1395
|
-
if (isCallbackProcessing()) return { completed: true };
|
|
1396
|
-
const u = getUrl();
|
|
1397
|
-
urlProvider.redirect(`${u.origin}${u.pathname}`);
|
|
1398
|
-
return { completed: true };
|
|
1399
|
-
}
|
|
1400
|
-
return { completed: true };
|
|
1401
|
-
}
|
|
1402
|
-
if (isCallbackUrlFromUrl(getUrl())) {
|
|
1403
|
-
const u = getUrl();
|
|
1404
|
-
urlProvider.redirect(`${u.origin}${u.pathname}`);
|
|
1405
|
-
}
|
|
1406
|
-
return { completed: true };
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
|
|
1410
|
-
// src/application/flows/AuthenticatedFlow.ts
|
|
1411
|
-
var AuthenticatedFlow = class {
|
|
1412
|
-
async execute(_context) {
|
|
1413
|
-
return { completed: true };
|
|
1414
|
-
}
|
|
1415
|
-
};
|
|
1416
|
-
|
|
1417
|
-
// src/application/engine/defaultEngine.ts
|
|
1418
|
-
var defaultEngine = null;
|
|
1419
|
-
function getDefaultAuthEngine() {
|
|
1420
|
-
return defaultEngine;
|
|
1421
|
-
}
|
|
1422
|
-
function setDefaultAuthEngine(engine) {
|
|
1423
|
-
defaultEngine = engine;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
// src/application/token.ts
|
|
1427
|
-
var lastNoRefreshLog = 0;
|
|
1428
|
-
var NO_REFRESH_LOG_INTERVAL_MS = 5e3;
|
|
1429
|
-
async function ensureValidAccessToken() {
|
|
1430
|
-
const engine = getDefaultAuthEngine();
|
|
1431
|
-
if (engine) {
|
|
1432
|
-
try {
|
|
1433
|
-
return await engine.getAccessToken();
|
|
1434
|
-
} catch {
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
const token = getAccessToken();
|
|
1438
|
-
if (token && !isAccessTokenExpired()) return token;
|
|
1439
|
-
const refreshToken = getRefreshToken();
|
|
1440
|
-
if (!refreshToken) {
|
|
1441
|
-
const now = Date.now();
|
|
1442
|
-
if (now - lastNoRefreshLog >= NO_REFRESH_LOG_INTERVAL_MS) {
|
|
1443
|
-
lastNoRefreshLog = now;
|
|
1444
|
-
logger.debug("No refresh token - cannot refresh; login required");
|
|
1445
|
-
}
|
|
1446
|
-
throw new Error("No refresh token; login required");
|
|
1447
|
-
}
|
|
1448
|
-
return await refreshAccessToken();
|
|
1449
|
-
}
|
|
1450
|
-
function getAccessToken2() {
|
|
1451
|
-
return getAccessToken();
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
// src/strategies/oauth2-pkce/pkce.ts
|
|
1455
|
-
function generateCodeVerifier(length = 128) {
|
|
1456
|
-
const array = new Uint8Array(length);
|
|
1457
|
-
crypto.getRandomValues(array);
|
|
1458
|
-
const base64 = btoa(String.fromCharCode(...array));
|
|
1459
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1460
|
-
}
|
|
1461
|
-
async function generateCodeChallenge(verifier) {
|
|
1462
|
-
try {
|
|
1463
|
-
const encoder = new TextEncoder();
|
|
1464
|
-
const data = encoder.encode(verifier);
|
|
1465
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
1466
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1467
|
-
const base64 = btoa(String.fromCharCode(...hashArray));
|
|
1468
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1469
|
-
} catch {
|
|
1470
|
-
throw new Error("Failed to generate code challenge");
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
function generateState() {
|
|
1474
|
-
const array = new Uint8Array(32);
|
|
1475
|
-
crypto.getRandomValues(array);
|
|
1476
|
-
const base64 = btoa(String.fromCharCode(...array));
|
|
1477
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
// src/utils/validation.ts
|
|
1481
|
-
function isValidGuid(guid) {
|
|
1482
|
-
if (!guid || typeof guid !== "string") {
|
|
1483
|
-
return false;
|
|
1484
|
-
}
|
|
1485
|
-
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1486
|
-
return guidRegex.test(guid.trim());
|
|
1487
|
-
}
|
|
1488
|
-
function validateTenantId(tenantId) {
|
|
1489
|
-
if (!tenantId || tenantId.trim() === "") {
|
|
1490
|
-
return {
|
|
1491
|
-
isValid: false,
|
|
1492
|
-
error: "Tenant ID is required"
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
if (!isValidGuid(tenantId)) {
|
|
1496
|
-
return {
|
|
1497
|
-
isValid: false,
|
|
1498
|
-
error: "Tenant ID must be a valid GUID format (00000000-0000-0000-0000-000000000001)"
|
|
1499
|
-
};
|
|
1500
|
-
}
|
|
1501
|
-
return { isValid: true };
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
// src/strategies/oauth2-pkce/oauth.ts
|
|
1505
|
-
function storeTenantConfig(config) {
|
|
1506
|
-
const validation = validateTenantId(config.tenantId);
|
|
1507
|
-
if (!validation.isValid) {
|
|
1508
|
-
logger.warn("Tenant ID validation failed", { tenantId: config.tenantId });
|
|
1509
|
-
}
|
|
1510
|
-
const authServerToStore = Config.getAuthServerUrl() || config.authServer;
|
|
1511
|
-
const redirectUriToStore = Config.getRedirectUri();
|
|
1512
|
-
const hostConfigKey = getStorageKey("host_config");
|
|
1513
|
-
let existing = {};
|
|
1514
|
-
try {
|
|
1515
|
-
const raw = getStorage(hostConfigKey);
|
|
1516
|
-
if (raw) existing = JSON.parse(raw);
|
|
1517
|
-
} catch {
|
|
1518
|
-
existing = {};
|
|
1519
|
-
}
|
|
1520
|
-
setStorage(
|
|
1521
|
-
hostConfigKey,
|
|
1522
|
-
JSON.stringify({
|
|
1523
|
-
...existing,
|
|
1524
|
-
authServerUrl: authServerToStore,
|
|
1525
|
-
clientId: config.client.clientId,
|
|
1526
|
-
redirectUri: redirectUriToStore,
|
|
1527
|
-
tenantId: config.tenantId,
|
|
1528
|
-
tenantKey: config.tenantKey ?? existing.tenantKey ?? "",
|
|
1529
|
-
tenantCode: config.tenantKey ?? existing.tenantCode ?? ""
|
|
1530
|
-
})
|
|
1531
|
-
);
|
|
1532
|
-
if (config.tenantKey) {
|
|
1533
|
-
setStorage(STORAGE_KEYS.tenantKey, config.tenantKey);
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
async function initiateAuthFlow(config) {
|
|
1537
|
-
try {
|
|
1538
|
-
logger.info("Initiating OAuth authorization flow");
|
|
1539
|
-
const authServerFromProps = Config.getAuthServerUrl();
|
|
1540
|
-
const authServerEffective = (authServerFromProps || config?.authServer || "").replace(/\/+$/, "");
|
|
1541
|
-
const redirectUriEffective = Config.getRedirectUri();
|
|
1542
|
-
const tenantIdEffective = config?.tenantId || "";
|
|
1543
|
-
const clientIdEffective = config?.client?.clientId || "";
|
|
1544
|
-
logger.debug("[initiateAuthFlow] Resolved inputs", {
|
|
1545
|
-
authServerFromProps: authServerFromProps ? "(provided)" : "(empty)",
|
|
1546
|
-
authServerEffective,
|
|
1547
|
-
redirectUriEffective,
|
|
1548
|
-
tenantIdEffective,
|
|
1549
|
-
clientIdEffective,
|
|
1550
|
-
scopes: config?.client?.scopes,
|
|
1551
|
-
hasRuntime: !!getAuthRuntime()
|
|
1552
|
-
});
|
|
1553
|
-
const token = getAccessToken();
|
|
1554
|
-
if (token && !isAccessTokenExpired()) {
|
|
1555
|
-
logger.debug("Valid token already exists, skipping auth flow");
|
|
1556
|
-
clearFlowFlags();
|
|
1557
|
-
return;
|
|
1558
|
-
}
|
|
1559
|
-
if (isCallbackUrl()) {
|
|
1560
|
-
logger.debug("Callback URL detected - clearing flow flag to allow callback processing");
|
|
1561
|
-
clearFlowFlags();
|
|
1562
|
-
return;
|
|
1563
|
-
}
|
|
1564
|
-
if (isFlowInProgress()) {
|
|
1565
|
-
if (isFlowFlagStale(3e3)) {
|
|
1566
|
-
logger.info("Stale auth flow flag detected, clearing and proceeding");
|
|
1567
|
-
clearFlowFlags();
|
|
1568
|
-
} else {
|
|
1569
|
-
logger.debug("Auth flow already in progress (recent), skipping duplicate redirect");
|
|
1570
|
-
return;
|
|
1772
|
+
if (tokenValid2) {
|
|
1773
|
+
removeStorage(STORAGE_KEYS.codeVerifier);
|
|
1774
|
+
removeStorage(STORAGE_KEYS.state);
|
|
1775
|
+
clearAllAuthFlags();
|
|
1776
|
+
markProcessed();
|
|
1777
|
+
cleanUrlSoft();
|
|
1778
|
+
logger.info("OAuth callback processed successfully (code already used, but token exists)");
|
|
1779
|
+
resetRedirectLoopGuard();
|
|
1780
|
+
emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
|
|
1781
|
+
return true;
|
|
1782
|
+
}
|
|
1571
1783
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1784
|
+
emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
|
|
1785
|
+
error: err.message,
|
|
1786
|
+
message: responseData?.error_description ?? err.message
|
|
1787
|
+
});
|
|
1788
|
+
logger.error("OAuth callback failed (Axios error)", {
|
|
1789
|
+
status,
|
|
1790
|
+
statusText: err.response?.statusText,
|
|
1791
|
+
responseData: err.response?.data,
|
|
1792
|
+
message: err.message,
|
|
1793
|
+
url: err.config?.url
|
|
1794
|
+
});
|
|
1795
|
+
} else {
|
|
1796
|
+
logger.error("OAuth callback failed", {
|
|
1797
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1580
1798
|
});
|
|
1581
|
-
throw new Error("Invalid tenant configuration for auth flow");
|
|
1582
1799
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
removeStorage(STORAGE_KEYS.state);
|
|
1593
|
-
setStorage(STORAGE_KEYS.codeVerifier, codeVerifier);
|
|
1594
|
-
setStorage(STORAGE_KEYS.state, state);
|
|
1595
|
-
const authUrl = buildAuthorizationUrl(config, codeChallenge, state);
|
|
1596
|
-
logger.debug("[initiateAuthFlow] Built authorization URL", {
|
|
1597
|
-
authServerEffective,
|
|
1598
|
-
urlPrefix: authUrl?.slice(0, 80)
|
|
1599
|
-
});
|
|
1600
|
-
if (!authUrl || !(authUrl.startsWith("http://") || authUrl.startsWith("https://"))) {
|
|
1601
|
-
throw new Error("Invalid authorization URL generated");
|
|
1800
|
+
clearAllAuthFlags();
|
|
1801
|
+
const existingToken = getAccessToken();
|
|
1802
|
+
const tokenValid = existingToken && !isAccessTokenExpired();
|
|
1803
|
+
if (tokenValid) {
|
|
1804
|
+
markProcessed();
|
|
1805
|
+
cleanUrlSoft();
|
|
1806
|
+
resetRedirectLoopGuard();
|
|
1807
|
+
emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
|
|
1808
|
+
return true;
|
|
1602
1809
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1810
|
+
emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
|
|
1811
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1812
|
+
});
|
|
1813
|
+
clearProcessing();
|
|
1814
|
+
if (isCallbackUrl()) cleanUrlSoft();
|
|
1815
|
+
return false;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// src/application/flows/CallbackFlow.ts
|
|
1820
|
+
var CallbackFlow = class {
|
|
1821
|
+
async execute(context) {
|
|
1822
|
+
const { runtime } = context;
|
|
1823
|
+
const { urlProvider, logger: log } = runtime;
|
|
1824
|
+
const getUrl = () => urlProvider.getCurrentUrl();
|
|
1825
|
+
clearFlowFlags();
|
|
1826
|
+
const handled = await handleCallback();
|
|
1827
|
+
if (handled) {
|
|
1828
|
+
if (isCallbackUrlFromUrl(getUrl())) {
|
|
1829
|
+
if (isCallbackProcessing()) return { completed: true };
|
|
1830
|
+
const u = getUrl();
|
|
1831
|
+
urlProvider.redirect(`${u.origin}${u.pathname}`);
|
|
1832
|
+
return { completed: true };
|
|
1833
|
+
}
|
|
1834
|
+
return { completed: true };
|
|
1607
1835
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
if (runtime) {
|
|
1612
|
-
logger.debug("[initiateAuthFlow] Redirect via runtime.urlProvider.redirect");
|
|
1613
|
-
runtime.urlProvider.redirect(authUrl);
|
|
1614
|
-
} else if (typeof window !== "undefined") {
|
|
1615
|
-
logger.debug("[initiateAuthFlow] Redirect via window.location.replace");
|
|
1616
|
-
window.location.replace(authUrl);
|
|
1617
|
-
} else {
|
|
1618
|
-
logger.error("[initiateAuthFlow] No runtime and no window; cannot redirect", { authUrl });
|
|
1836
|
+
if (isCallbackUrlFromUrl(getUrl())) {
|
|
1837
|
+
const u = getUrl();
|
|
1838
|
+
urlProvider.redirect(`${u.origin}${u.pathname}`);
|
|
1619
1839
|
}
|
|
1620
|
-
|
|
1621
|
-
clearFlowFlags();
|
|
1622
|
-
logger.error("Failed to initiate auth flow", {
|
|
1623
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1624
|
-
});
|
|
1625
|
-
throw error;
|
|
1840
|
+
return { completed: true };
|
|
1626
1841
|
}
|
|
1842
|
+
};
|
|
1843
|
+
|
|
1844
|
+
// src/application/flows/AuthenticatedFlow.ts
|
|
1845
|
+
var AuthenticatedFlow = class {
|
|
1846
|
+
async execute(_context) {
|
|
1847
|
+
return { completed: true };
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1851
|
+
// src/application/engine/defaultEngine.ts
|
|
1852
|
+
var defaultEngine = null;
|
|
1853
|
+
function getDefaultAuthEngine() {
|
|
1854
|
+
return defaultEngine;
|
|
1627
1855
|
}
|
|
1628
|
-
function
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1856
|
+
function setDefaultAuthEngine(engine) {
|
|
1857
|
+
defaultEngine = engine;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// src/application/token.ts
|
|
1861
|
+
var lastNoRefreshLog = 0;
|
|
1862
|
+
var NO_REFRESH_LOG_INTERVAL_MS = 5e3;
|
|
1863
|
+
async function ensureValidAccessToken() {
|
|
1864
|
+
const engine = getDefaultAuthEngine();
|
|
1865
|
+
if (engine) {
|
|
1866
|
+
try {
|
|
1867
|
+
return await engine.getAccessToken();
|
|
1868
|
+
} catch {
|
|
1869
|
+
}
|
|
1637
1870
|
}
|
|
1638
|
-
const
|
|
1639
|
-
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1871
|
+
const token = getAccessToken();
|
|
1872
|
+
if (token && !isAccessTokenExpired()) return token;
|
|
1873
|
+
const refreshToken = getRefreshToken();
|
|
1874
|
+
if (!refreshToken) {
|
|
1875
|
+
const now = Date.now();
|
|
1876
|
+
if (now - lastNoRefreshLog >= NO_REFRESH_LOG_INTERVAL_MS) {
|
|
1877
|
+
lastNoRefreshLog = now;
|
|
1878
|
+
logger.debug("No refresh token - cannot refresh; login required");
|
|
1879
|
+
}
|
|
1880
|
+
throw new Error("No refresh token; login required");
|
|
1881
|
+
}
|
|
1882
|
+
return await refreshAccessToken();
|
|
1883
|
+
}
|
|
1884
|
+
function getAccessToken2() {
|
|
1885
|
+
return getAccessToken();
|
|
1652
1886
|
}
|
|
1653
1887
|
|
|
1654
1888
|
// src/application/flows/RefreshFlow.ts
|
|
@@ -1729,13 +1963,18 @@ var TenantSwitchFlow = class {
|
|
|
1729
1963
|
);
|
|
1730
1964
|
}
|
|
1731
1965
|
try {
|
|
1732
|
-
const cfg =
|
|
1733
|
-
|
|
1734
|
-
|
|
1966
|
+
const cfg = getAuthConfig2();
|
|
1967
|
+
writeHostConfigCache({
|
|
1968
|
+
apiBaseUrl: cfg.apiBaseUrl,
|
|
1969
|
+
authServerUrl: cfg.authServerUrl,
|
|
1970
|
+
callbackUrl: cfg.callbackUrl,
|
|
1971
|
+
appId: cfg.appId,
|
|
1972
|
+
clientCode: tenantConfig.client?.clientCode ?? cfg.clientCode ?? "",
|
|
1973
|
+
clientId: cfg.clientId || tenantConfig.client?.clientId,
|
|
1974
|
+
tenantId: tenantConfig.tenantId ?? cfg.tenantId,
|
|
1735
1975
|
tenantKey,
|
|
1736
|
-
tenantCode: tenantKey
|
|
1737
|
-
|
|
1738
|
-
}));
|
|
1976
|
+
tenantCode: tenantKey
|
|
1977
|
+
});
|
|
1739
1978
|
} catch {
|
|
1740
1979
|
}
|
|
1741
1980
|
setTenantKey(tenantKey);
|
|
@@ -1899,6 +2138,22 @@ async function bootstrap() {
|
|
|
1899
2138
|
return bootstrapImpl();
|
|
1900
2139
|
}
|
|
1901
2140
|
|
|
2141
|
+
// src/infrastructure/watchers/watcher-guard.ts
|
|
2142
|
+
var suspended = false;
|
|
2143
|
+
var generation = 0;
|
|
2144
|
+
function suspendWatchers() {
|
|
2145
|
+
suspended = true;
|
|
2146
|
+
generation += 1;
|
|
2147
|
+
return generation;
|
|
2148
|
+
}
|
|
2149
|
+
function resumeWatchers(expectedGen) {
|
|
2150
|
+
if (expectedGen !== void 0 && expectedGen !== generation) return;
|
|
2151
|
+
suspended = false;
|
|
2152
|
+
}
|
|
2153
|
+
function areWatchersSuspended() {
|
|
2154
|
+
return suspended;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
1902
2157
|
// src/strategies/cookie/cookie-auth.ts
|
|
1903
2158
|
var COOKIE_NAMES = {
|
|
1904
2159
|
accessToken: "tenneo_auth_access_token",
|
|
@@ -2025,6 +2280,10 @@ function checkCookiesDeleted() {
|
|
|
2025
2280
|
}
|
|
2026
2281
|
}
|
|
2027
2282
|
function handleCookieDeletion() {
|
|
2283
|
+
if (areWatchersSuspended()) {
|
|
2284
|
+
logger.debug("Cookie deletion handler skipped \u2014 watchers suspended (logout/teardown)");
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2028
2287
|
try {
|
|
2029
2288
|
logger.info("Cookies deleted detected - clearing session and redirecting to login");
|
|
2030
2289
|
clearAuthStorage();
|
|
@@ -2062,6 +2321,7 @@ function startCookieWatcher() {
|
|
|
2062
2321
|
allCookies: initialCookies
|
|
2063
2322
|
});
|
|
2064
2323
|
watchInterval = setInterval(async () => {
|
|
2324
|
+
if (areWatchersSuspended()) return;
|
|
2065
2325
|
const cookiesDeleted = checkCookiesDeleted();
|
|
2066
2326
|
if (cookiesDeleted) {
|
|
2067
2327
|
logger.info("Cookies deleted detected by cookie watcher");
|
|
@@ -2125,9 +2385,11 @@ function checkStorageCleared() {
|
|
|
2125
2385
|
}
|
|
2126
2386
|
}
|
|
2127
2387
|
function handleStorageEvent(event) {
|
|
2388
|
+
if (areWatchersSuspended()) return;
|
|
2128
2389
|
if (typeof window !== "undefined" && event.storageArea === window.sessionStorage) {
|
|
2129
2390
|
const wasCleared = checkStorageCleared();
|
|
2130
2391
|
if (wasCleared) {
|
|
2392
|
+
if (areWatchersSuspended()) return;
|
|
2131
2393
|
logger.info("Detected manual sessionStorage clear! Redirecting to login...");
|
|
2132
2394
|
const cb = getSessionClearedCallback();
|
|
2133
2395
|
if (cb) {
|
|
@@ -2147,8 +2409,10 @@ function startStorageWatcher() {
|
|
|
2147
2409
|
storageEventListener = handleStorageEvent;
|
|
2148
2410
|
window.addEventListener("storage", storageEventListener);
|
|
2149
2411
|
const pollInterval = setInterval(() => {
|
|
2412
|
+
if (areWatchersSuspended()) return;
|
|
2150
2413
|
const wasCleared = checkStorageCleared();
|
|
2151
2414
|
if (wasCleared) {
|
|
2415
|
+
if (areWatchersSuspended()) return;
|
|
2152
2416
|
logger.info("Detected manual sessionStorage clear (polling)! Redirecting to login...");
|
|
2153
2417
|
clearInterval(pollInterval);
|
|
2154
2418
|
const cb = getSessionClearedCallback();
|
|
@@ -2219,27 +2483,6 @@ function buildUrl(baseUrl, path) {
|
|
|
2219
2483
|
url.pathname = url.pathname.endsWith("/") ? `${url.pathname}${normalizedPath.slice(1)}` : `${url.pathname}${normalizedPath}`;
|
|
2220
2484
|
return url.toString();
|
|
2221
2485
|
}
|
|
2222
|
-
function snapshotHostConfig() {
|
|
2223
|
-
try {
|
|
2224
|
-
const raw = getStorage(getStorageKey("host_config"));
|
|
2225
|
-
if (!raw) return null;
|
|
2226
|
-
const parsed = JSON.parse(raw);
|
|
2227
|
-
return parsed && typeof parsed === "object" ? parsed : null;
|
|
2228
|
-
} catch {
|
|
2229
|
-
return null;
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
function restoreHostConfig(config) {
|
|
2233
|
-
if (!config) return;
|
|
2234
|
-
try {
|
|
2235
|
-
setStorage(getStorageKey("host_config"), JSON.stringify(config));
|
|
2236
|
-
logger.debug("Restored host_config after logout cleanup");
|
|
2237
|
-
} catch (error) {
|
|
2238
|
-
logger.warn("Failed to restore host_config after logout cleanup", {
|
|
2239
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
2486
|
function getCsrfToken() {
|
|
2244
2487
|
try {
|
|
2245
2488
|
if (typeof document === "undefined") return null;
|
|
@@ -2254,62 +2497,68 @@ function getCsrfToken() {
|
|
|
2254
2497
|
}
|
|
2255
2498
|
async function callServerLogout(logoutUrl) {
|
|
2256
2499
|
const csrfToken = getCsrfToken();
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
if (csrfToken) {
|
|
2267
|
-
headers["X-CSRF-Token"] = csrfToken;
|
|
2268
|
-
}
|
|
2500
|
+
const headers = {
|
|
2501
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
2502
|
+
Accept: "application/json, */*",
|
|
2503
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
2504
|
+
};
|
|
2505
|
+
if (csrfToken) {
|
|
2506
|
+
headers["X-CSRF-Token"] = csrfToken;
|
|
2507
|
+
}
|
|
2508
|
+
try {
|
|
2269
2509
|
const response = await fetch(logoutUrl, {
|
|
2270
|
-
method,
|
|
2510
|
+
method: "POST",
|
|
2271
2511
|
mode: "cors",
|
|
2272
2512
|
headers,
|
|
2273
|
-
credentials: "include"
|
|
2513
|
+
credentials: "include",
|
|
2514
|
+
body: ""
|
|
2274
2515
|
});
|
|
2275
2516
|
if (!response.ok) {
|
|
2276
|
-
logger.warn("Server logout HTTP error", {
|
|
2277
|
-
method,
|
|
2517
|
+
logger.warn("[logout] Server logout HTTP error", {
|
|
2278
2518
|
status: response.status,
|
|
2279
2519
|
statusText: response.statusText
|
|
2280
2520
|
});
|
|
2281
2521
|
return { success: false };
|
|
2282
2522
|
}
|
|
2283
|
-
const
|
|
2284
|
-
|
|
2523
|
+
const contentType = response.headers.get("content-type") || "";
|
|
2524
|
+
if (!contentType.includes("application/json")) {
|
|
2525
|
+
logger.info("[logout] Server logout: OK without JSON body", {
|
|
2526
|
+
status: response.status
|
|
2527
|
+
});
|
|
2528
|
+
return { success: true, response: { success: true } };
|
|
2529
|
+
}
|
|
2530
|
+
const text = await response.text();
|
|
2531
|
+
let data = {};
|
|
2532
|
+
if (text?.trim()) {
|
|
2533
|
+
try {
|
|
2534
|
+
data = JSON.parse(text);
|
|
2535
|
+
} catch {
|
|
2536
|
+
logger.warn("[logout] Server returned non-JSON body with JSON content-type");
|
|
2537
|
+
return { success: false };
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
logger.info("[logout] Server logout response", { response: data });
|
|
2285
2541
|
if (data && (data.success === true || data.logout === true)) {
|
|
2286
2542
|
return { success: true, response: data };
|
|
2287
2543
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
});
|
|
2544
|
+
if (!text || text.trim() === "") {
|
|
2545
|
+
return { success: true, response: { success: true } };
|
|
2546
|
+
}
|
|
2547
|
+
logger.warn("[logout] Server logout: unexpected JSON payload", { response: data });
|
|
2292
2548
|
return { success: false, response: data };
|
|
2293
|
-
}
|
|
2294
|
-
try {
|
|
2295
|
-
const postResult = await doRequest("POST");
|
|
2296
|
-
if (postResult.success) return postResult;
|
|
2297
|
-
logger.debug("POST logout did not succeed, trying GET");
|
|
2298
|
-
const getResult = await doRequest("GET");
|
|
2299
|
-
return getResult;
|
|
2300
2549
|
} catch (error) {
|
|
2301
|
-
logger.warn("Server logout failed
|
|
2550
|
+
logger.warn("[logout] Server logout request failed", {
|
|
2302
2551
|
error: error instanceof Error ? error.message : String(error)
|
|
2303
2552
|
});
|
|
2304
2553
|
return { success: false };
|
|
2305
2554
|
}
|
|
2306
2555
|
}
|
|
2307
2556
|
function performLocalLogoutCleanup() {
|
|
2308
|
-
logger.debug("
|
|
2557
|
+
logger.debug("[logout] Local cleanup: cookies + auth storage");
|
|
2309
2558
|
try {
|
|
2310
2559
|
getAuthRuntime()?.clearCookies?.clearCookies();
|
|
2311
2560
|
} catch (error) {
|
|
2312
|
-
logger.debug("Runtime cookie clearer failed (ignored)", {
|
|
2561
|
+
logger.debug("[logout] Runtime cookie clearer failed (ignored)", {
|
|
2313
2562
|
error: error instanceof Error ? error.message : String(error)
|
|
2314
2563
|
});
|
|
2315
2564
|
}
|
|
@@ -2317,11 +2566,11 @@ function performLocalLogoutCleanup() {
|
|
|
2317
2566
|
try {
|
|
2318
2567
|
clearAuthStorage();
|
|
2319
2568
|
} catch (error) {
|
|
2320
|
-
logger.warn("clearAuthStorage failed
|
|
2569
|
+
logger.warn("[logout] clearAuthStorage failed", {
|
|
2321
2570
|
error: error instanceof Error ? error.message : String(error)
|
|
2322
2571
|
});
|
|
2323
2572
|
}
|
|
2324
|
-
logger.info("Local
|
|
2573
|
+
logger.info("[logout] Local cleanup completed");
|
|
2325
2574
|
}
|
|
2326
2575
|
function acquireLogoutLock() {
|
|
2327
2576
|
const LOCK_KEY = getStorageKey("logout_lock");
|
|
@@ -2330,7 +2579,7 @@ function acquireLogoutLock() {
|
|
|
2330
2579
|
if (existingLock) {
|
|
2331
2580
|
const lockTime = Number(existingLock);
|
|
2332
2581
|
if (!Number.isNaN(lockTime) && now - lockTime < 5e3) {
|
|
2333
|
-
logger.debug("
|
|
2582
|
+
logger.debug("[logout] Lock active \u2014 duplicate tab logout skipped", {
|
|
2334
2583
|
now,
|
|
2335
2584
|
lockTime
|
|
2336
2585
|
});
|
|
@@ -2346,72 +2595,95 @@ function releaseLogoutLock() {
|
|
|
2346
2595
|
} catch {
|
|
2347
2596
|
}
|
|
2348
2597
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
tenantCode: tenantKey.trim()
|
|
2356
|
-
};
|
|
2357
|
-
setStorage(getStorageKey("host_config"), JSON.stringify(updated));
|
|
2358
|
-
}
|
|
2598
|
+
var logoutInFlight = null;
|
|
2599
|
+
async function runLogoutPipeline(tenantKey) {
|
|
2600
|
+
const gen = suspendWatchers();
|
|
2601
|
+
logger.info("[logout] Lifecycle: suspend watchers", { generation: gen });
|
|
2602
|
+
stopStorageWatcher();
|
|
2603
|
+
stopCookieWatcher();
|
|
2359
2604
|
if (!acquireLogoutLock()) {
|
|
2360
|
-
|
|
2605
|
+
resumeWatchers(gen);
|
|
2606
|
+
logger.debug("[logout] Lifecycle: resume (skipped \u2014 lock)");
|
|
2607
|
+
return { success: true, message: "Logout already in progress in another tab." };
|
|
2361
2608
|
}
|
|
2609
|
+
let serverLogoutSuccess = false;
|
|
2610
|
+
let serverResponse;
|
|
2362
2611
|
try {
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2612
|
+
if (tenantKey?.trim()) {
|
|
2613
|
+
applyRuntimeTenantSwitch(tenantKey.trim(), { syncStorageCache: true });
|
|
2614
|
+
logger.info("[logout] Runtime tenant applied for logout context", {
|
|
2615
|
+
tenantKey: tenantKey.trim()
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
const authConfig = getAuthConfig2();
|
|
2619
|
+
const apiAuthBaseUrl = authConfig.authServerUrl;
|
|
2369
2620
|
if (apiAuthBaseUrl) {
|
|
2370
2621
|
const logoutUrl = buildUrl(apiAuthBaseUrl, "account/logout");
|
|
2371
|
-
logger.info("Calling auth server logout", { logoutUrl });
|
|
2622
|
+
logger.info("[logout] Calling auth server logout (POST)", { logoutUrl });
|
|
2372
2623
|
const logoutResult = await callServerLogout(logoutUrl);
|
|
2373
2624
|
serverLogoutSuccess = logoutResult.success;
|
|
2374
2625
|
serverResponse = logoutResult.response;
|
|
2375
2626
|
if (!serverLogoutSuccess) {
|
|
2376
|
-
logger.warn("Server logout
|
|
2627
|
+
logger.warn("[logout] Server logout did not confirm success \u2014 clearing local session anyway", {
|
|
2377
2628
|
logoutUrl,
|
|
2378
2629
|
serverResponse
|
|
2379
2630
|
});
|
|
2380
2631
|
}
|
|
2381
2632
|
} else {
|
|
2382
|
-
logger.warn("
|
|
2633
|
+
logger.warn("[logout] authServerUrl missing \u2014 local logout only");
|
|
2383
2634
|
}
|
|
2635
|
+
} catch (error) {
|
|
2636
|
+
logger.error("[logout] Unexpected error before local cleanup", {
|
|
2637
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2638
|
+
});
|
|
2639
|
+
}
|
|
2640
|
+
try {
|
|
2384
2641
|
performLocalLogoutCleanup();
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
releaseLogoutLock();
|
|
2389
|
-
logger.info("Logout completed \u2013 flags set, storage cleared, fresh login required", {
|
|
2390
|
-
serverLogoutSuccess,
|
|
2391
|
-
serverResponse
|
|
2642
|
+
} catch (error) {
|
|
2643
|
+
logger.error("[logout] Local cleanup threw", {
|
|
2644
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2392
2645
|
});
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2646
|
+
}
|
|
2647
|
+
try {
|
|
2648
|
+
syncHostConfigCacheFromMemory();
|
|
2649
|
+
logger.debug("[logout] host_config cache refreshed from runtime (not restored from snapshot)");
|
|
2650
|
+
} catch (error) {
|
|
2651
|
+
logger.warn("[logout] Failed to sync host config cache", {
|
|
2652
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2397
2653
|
});
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2654
|
+
}
|
|
2655
|
+
try {
|
|
2656
|
+
resetCookieWatcher();
|
|
2657
|
+
startStorageWatcher();
|
|
2658
|
+
logger.info("[logout] Watchers restarted");
|
|
2403
2659
|
} catch (error) {
|
|
2404
|
-
logger.
|
|
2660
|
+
logger.warn("[logout] Watcher restart failed", {
|
|
2405
2661
|
error: error instanceof Error ? error.message : String(error)
|
|
2406
2662
|
});
|
|
2407
|
-
performLocalLogoutCleanup();
|
|
2408
|
-
restoreHostConfig(preservedHostConfig);
|
|
2409
|
-
releaseLogoutLock();
|
|
2410
|
-
return {
|
|
2411
|
-
success: true,
|
|
2412
|
-
message: "Logged out locally due to an error. Fresh login required."
|
|
2413
|
-
};
|
|
2414
2663
|
}
|
|
2664
|
+
releaseLogoutLock();
|
|
2665
|
+
resumeWatchers(gen);
|
|
2666
|
+
logger.info("[logout] Lifecycle: complete", { serverLogoutSuccess });
|
|
2667
|
+
emitAuthEvent(AuthEventNames.LOGOUT_COMPLETED, {
|
|
2668
|
+
serverLogoutSuccess,
|
|
2669
|
+
tenantKey: tenantKey?.trim() || getAuthConfig2().tenantCode || void 0,
|
|
2670
|
+
message: serverLogoutSuccess ? "Server logout success" : "Local logout (server may be pending)"
|
|
2671
|
+
});
|
|
2672
|
+
return {
|
|
2673
|
+
success: true,
|
|
2674
|
+
message: serverLogoutSuccess ? serverResponse?.message || "Logout successful. Fresh login required." : "Logged out locally. Fresh login required (server logout may have failed).",
|
|
2675
|
+
serverResponse
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
async function logout(tenantKey) {
|
|
2679
|
+
if (logoutInFlight) {
|
|
2680
|
+
logger.debug("[logout] Coalescing with in-flight logout");
|
|
2681
|
+
return logoutInFlight;
|
|
2682
|
+
}
|
|
2683
|
+
logoutInFlight = runLogoutPipeline(tenantKey).finally(() => {
|
|
2684
|
+
logoutInFlight = null;
|
|
2685
|
+
});
|
|
2686
|
+
return logoutInFlight;
|
|
2415
2687
|
}
|
|
2416
2688
|
var AuthContext = react.createContext(void 0);
|
|
2417
2689
|
function useAuthContext() {
|
|
@@ -2486,7 +2758,6 @@ async function resolveTenant(tenantKey) {
|
|
|
2486
2758
|
scopes: DEFAULT_SCOPES2
|
|
2487
2759
|
}
|
|
2488
2760
|
};
|
|
2489
|
-
console.log("config====", config);
|
|
2490
2761
|
logger.debug("Resolved tenant config", { tenantCode });
|
|
2491
2762
|
return config;
|
|
2492
2763
|
} catch (error) {
|
|
@@ -2747,33 +3018,29 @@ function AuthProvider({
|
|
|
2747
3018
|
logger.warn("[AuthProvider] callbackUrl missing from host props");
|
|
2748
3019
|
}
|
|
2749
3020
|
setConfig({
|
|
2750
|
-
...mergedTenantCode ? { tenantCode: mergedTenantCode } : {},
|
|
3021
|
+
...mergedTenantCode ? { tenantCode: mergedTenantCode, tenantKey: mergedTenantCode } : {},
|
|
2751
3022
|
...mergedApiBaseUrl ? { apiBaseUrl: mergedApiBaseUrl } : {},
|
|
2752
3023
|
...mergedAuthServerUrl ? { authServerUrl: mergedAuthServerUrl } : {},
|
|
2753
3024
|
...mergedCallbackUrl ? { callbackUrl: mergedCallbackUrl } : {}
|
|
2754
3025
|
});
|
|
3026
|
+
logConfigResolutionDebug("AuthProvider host props applied");
|
|
2755
3027
|
return () => {
|
|
2756
3028
|
clearConfigOverrides();
|
|
2757
3029
|
};
|
|
2758
3030
|
}, [mergedTenantCode, mergedApiBaseUrl, mergedAuthServerUrl, mergedCallbackUrl]);
|
|
2759
3031
|
react.useEffect(() => {
|
|
2760
3032
|
try {
|
|
2761
|
-
|
|
2762
|
-
const raw = getStorage(hostConfigKey);
|
|
2763
|
-
const existing = raw ? JSON.parse(raw) : {};
|
|
2764
|
-
const payload = {
|
|
2765
|
-
...existing,
|
|
3033
|
+
writeHostConfigCache({
|
|
2766
3034
|
tenantCode: mergedTenantCode ?? "",
|
|
2767
3035
|
tenantKey: mergedTenantCode ?? "",
|
|
2768
3036
|
appId: appId ?? "",
|
|
2769
3037
|
apiBaseUrl: mergedApiBaseUrl ?? "",
|
|
2770
3038
|
authServerUrl: mergedAuthServerUrl ?? "",
|
|
2771
3039
|
callbackUrl: mergedCallbackUrl ?? ""
|
|
2772
|
-
};
|
|
2773
|
-
setStorage(hostConfigKey, JSON.stringify(payload));
|
|
3040
|
+
});
|
|
2774
3041
|
} catch {
|
|
2775
3042
|
}
|
|
2776
|
-
}, [mergedTenantCode, mergedApiBaseUrl, mergedAuthServerUrl, mergedCallbackUrl]);
|
|
3043
|
+
}, [mergedTenantCode, mergedApiBaseUrl, mergedAuthServerUrl, mergedCallbackUrl, appId]);
|
|
2777
3044
|
react.useLayoutEffect(() => {
|
|
2778
3045
|
setStorageKeyPrefix(resolvedTenantKey ?? "");
|
|
2779
3046
|
const adapter = storageAdapter !== void 0 && storageAdapter !== null ? storageAdapter : createSecureSessionStorageAdapter(namespace);
|
|
@@ -2839,14 +3106,14 @@ function AuthProvider({
|
|
|
2839
3106
|
logger.debug("[AuthProvider] TenantKey unchanged", { tenantKey: resolvedTenantKey2 });
|
|
2840
3107
|
}
|
|
2841
3108
|
try {
|
|
2842
|
-
|
|
2843
|
-
const raw = getStorage(hostConfigKey);
|
|
2844
|
-
const existing = raw ? JSON.parse(raw) : {};
|
|
2845
|
-
setStorage(hostConfigKey, JSON.stringify({
|
|
2846
|
-
...existing,
|
|
3109
|
+
writeHostConfigCache({
|
|
2847
3110
|
tenantKey: resolvedTenantKey2,
|
|
2848
3111
|
tenantCode: resolvedTenantKey2
|
|
2849
|
-
})
|
|
3112
|
+
});
|
|
3113
|
+
logConfigResolutionDebug("AuthProvider tenant key synced to cache", {
|
|
3114
|
+
tenantCode: resolvedTenantKey2,
|
|
3115
|
+
tenantKey: resolvedTenantKey2
|
|
3116
|
+
});
|
|
2850
3117
|
} catch {
|
|
2851
3118
|
}
|
|
2852
3119
|
setStorage(getStorageKey("tenant_key"), resolvedTenantKey2);
|
|
@@ -3251,6 +3518,7 @@ function useAuthInit() {
|
|
|
3251
3518
|
}
|
|
3252
3519
|
|
|
3253
3520
|
exports.AuthCallback = AuthCallback;
|
|
3521
|
+
exports.AuthConfigError = AuthConfigError;
|
|
3254
3522
|
exports.AuthEventNames = AuthEventNames;
|
|
3255
3523
|
exports.AuthProvider = AuthProvider;
|
|
3256
3524
|
exports.Config = Config;
|
|
@@ -3259,6 +3527,8 @@ exports.ProtectedRoute = ProtectedRoute;
|
|
|
3259
3527
|
exports.STORAGE_KEYS = STORAGE_KEYS2;
|
|
3260
3528
|
exports.STORAGE_KEY_PREFIX = STORAGE_KEY_PREFIX;
|
|
3261
3529
|
exports.SanitizedError = SanitizedError;
|
|
3530
|
+
exports.applyRuntimeTenantSwitch = applyRuntimeTenantSwitch;
|
|
3531
|
+
exports.areWatchersSuspended = areWatchersSuspended;
|
|
3262
3532
|
exports.bootstrap = bootstrap;
|
|
3263
3533
|
exports.buildStorageKey = buildStorageKey;
|
|
3264
3534
|
exports.createAuthEngine = createAuthEngine;
|
|
@@ -3271,28 +3541,41 @@ exports.enforceHttps = enforceHttps;
|
|
|
3271
3541
|
exports.ensureValidAccessToken = ensureValidAccessToken;
|
|
3272
3542
|
exports.extractTenantKey = extractTenantKey;
|
|
3273
3543
|
exports.getAccessToken = getAccessToken2;
|
|
3274
|
-
exports.getAuthConfig =
|
|
3544
|
+
exports.getAuthConfig = getAuthConfig2;
|
|
3275
3545
|
exports.getAuthRuntime = getAuthRuntime;
|
|
3276
3546
|
exports.getConfig = getConfig;
|
|
3547
|
+
exports.getConfigFieldSources = getConfigFieldSources;
|
|
3548
|
+
exports.getEffectiveTenantCode = getEffectiveTenantCode;
|
|
3549
|
+
exports.getEffectiveTenantKey = getEffectiveTenantKey;
|
|
3277
3550
|
exports.getStorageAdapter = getStorageAdapter;
|
|
3278
3551
|
exports.getStorageNamespace = getStorageNamespace;
|
|
3279
3552
|
exports.getStoredAccessToken = getAccessToken;
|
|
3553
|
+
exports.initPlugin = initPlugin;
|
|
3280
3554
|
exports.isAccessTokenExpired = isAccessTokenExpired;
|
|
3281
3555
|
exports.isCallbackUrl = isCallbackUrl;
|
|
3556
|
+
exports.logConfigResolutionDebug = logConfigResolutionDebug;
|
|
3282
3557
|
exports.logout = logout;
|
|
3283
3558
|
exports.refreshAccessToken = refreshAccessToken;
|
|
3559
|
+
exports.requireAuthConfig = requireAuthConfig;
|
|
3284
3560
|
exports.resetCookieWatcher = resetCookieWatcher;
|
|
3285
3561
|
exports.resolveConfig = resolveConfig;
|
|
3286
3562
|
exports.resolveTenant = resolveTenant;
|
|
3563
|
+
exports.resumeWatchers = resumeWatchers;
|
|
3287
3564
|
exports.sanitizeErrorMessage = sanitizeErrorMessage;
|
|
3288
3565
|
exports.setAuthRuntime = setAuthRuntime;
|
|
3289
3566
|
exports.setConfig = setConfig;
|
|
3567
|
+
exports.setHostConfigProvider = setHostConfigProvider;
|
|
3290
3568
|
exports.setStorageContext = setStorageContext;
|
|
3291
3569
|
exports.startCookieWatcher = startCookieWatcher;
|
|
3570
|
+
exports.startStorageWatcher = startStorageWatcher;
|
|
3292
3571
|
exports.stopCookieWatcher = stopCookieWatcher;
|
|
3572
|
+
exports.stopStorageWatcher = stopStorageWatcher;
|
|
3293
3573
|
exports.subscribeAuthEvent = subscribeAuthEvent;
|
|
3574
|
+
exports.suspendWatchers = suspendWatchers;
|
|
3575
|
+
exports.syncHostConfigCacheFromMemory = syncHostConfigCacheFromMemory;
|
|
3294
3576
|
exports.useAuth = useAuth;
|
|
3295
3577
|
exports.useAuthContext = useAuthContext;
|
|
3296
3578
|
exports.useAuthInit = useAuthInit;
|
|
3579
|
+
exports.writeHostConfigCache = writeHostConfigCache;
|
|
3297
3580
|
//# sourceMappingURL=index.js.map
|
|
3298
3581
|
//# sourceMappingURL=index.js.map
|