tenneo-auth-plugin 0.1.0

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.mjs ADDED
@@ -0,0 +1,3385 @@
1
+ import { z } from 'zod';
2
+ import axios from 'axios';
3
+ import { createContext, useContext, useMemo, useRef, useLayoutEffect, useEffect, useState, useCallback } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ // src/infrastructure/runtime/context.ts
7
+ var currentRuntime = null;
8
+ function setAuthRuntime(runtime) {
9
+ currentRuntime = runtime;
10
+ }
11
+ function getAuthRuntime() {
12
+ return currentRuntime;
13
+ }
14
+
15
+ // src/domain/tenant.ts
16
+ function isLocalhost(hostname) {
17
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.endsWith(".local");
18
+ }
19
+ function extractTenantKeyFromDomain(urlOrHostname, config) {
20
+ try {
21
+ let hostname;
22
+ if (urlOrHostname.startsWith("http://") || urlOrHostname.startsWith("https://")) {
23
+ try {
24
+ const url = new URL(urlOrHostname);
25
+ hostname = url.hostname;
26
+ } catch {
27
+ hostname = urlOrHostname.replace(/^https?:\/\//, "").split("/")[0];
28
+ }
29
+ } else {
30
+ hostname = urlOrHostname;
31
+ }
32
+ const parts = hostname.split(".");
33
+ if (parts.length < 2) return config.getClientCode();
34
+ const tenantKey = parts[0];
35
+ if (!tenantKey || tenantKey.trim() === "") return config.getClientCode();
36
+ if (tenantKey.trim().toLowerCase() === "productlms") return config.getClientCode();
37
+ return tenantKey.trim();
38
+ } catch {
39
+ return config.getClientCode();
40
+ }
41
+ }
42
+ function extractTenantKeyFromUrl(url, config, getStorage3, getKey) {
43
+ try {
44
+ const propsConfigKey = getKey ? getKey("props_config") : "tenneo_auth_props_config";
45
+ const propsConfigStr = getStorage3(propsConfigKey);
46
+ if (propsConfigStr) {
47
+ try {
48
+ const propsConfig = JSON.parse(propsConfigStr);
49
+ if (propsConfig.tenantKey?.trim()) return propsConfig.tenantKey.trim();
50
+ } catch {
51
+ }
52
+ }
53
+ const tenantKeyStorageKey = getKey ? getKey("tenant_key") : "tenneo_auth_tenant_key";
54
+ const storedTenantKey = getStorage3(tenantKeyStorageKey);
55
+ if (storedTenantKey?.trim()) return storedTenantKey.trim();
56
+ const urlParams = new URLSearchParams(url.search);
57
+ const tenantKeyFromQuery = urlParams.get("t");
58
+ if (tenantKeyFromQuery?.trim()) {
59
+ const hostnameParts = url.hostname.split(".");
60
+ const baseDomain = hostnameParts.length >= 2 ? hostnameParts.slice(-2).join(".") : url.hostname;
61
+ const simulatedHostname = `${tenantKeyFromQuery.trim()}.${baseDomain}`;
62
+ const key = extractTenantKeyFromDomain(simulatedHostname, config);
63
+ if (key) return key;
64
+ return tenantKeyFromQuery.trim();
65
+ }
66
+ const keyFromHost = extractTenantKeyFromDomain(url.hostname, config);
67
+ if (keyFromHost) return keyFromHost;
68
+ const clientCode = config.getClientCode();
69
+ if (clientCode?.trim()) return clientCode.trim();
70
+ const tenantId = config.getTenantId();
71
+ if (tenantId?.trim()) return tenantId.trim();
72
+ return config.getClientCode();
73
+ } catch {
74
+ const clientCode = config.getClientCode();
75
+ if (clientCode?.trim()) return clientCode.trim();
76
+ return config.getTenantId() || config.getClientCode();
77
+ }
78
+ }
79
+ function isCallbackUrlFromUrl(url) {
80
+ const params = new URLSearchParams(url.search);
81
+ return params.has("code") && params.has("state");
82
+ }
83
+
84
+ // src/infrastructure/storage/prefix.ts
85
+ var DEFAULT_PREFIX = "tenneo_auth_";
86
+ var currentPrefix = DEFAULT_PREFIX;
87
+ function getStorageKeyPrefix() {
88
+ return currentPrefix;
89
+ }
90
+ function normalizeTenantCode(tenantCode) {
91
+ const trimmed = tenantCode.trim();
92
+ if (!trimmed) return "";
93
+ return trimmed.replace(/[^a-zA-Z0-9_]/g, "_").toLowerCase();
94
+ }
95
+ function setStorageKeyPrefix(tenantCode) {
96
+ const normalized = normalizeTenantCode(tenantCode);
97
+ currentPrefix = normalized ? `${normalized}_auth_` : DEFAULT_PREFIX;
98
+ }
99
+ function getStorageKey(suffix) {
100
+ return getStorageKeyPrefix() + suffix;
101
+ }
102
+
103
+ // src/infrastructure/storage/namespace.ts
104
+ function buildStorageKey(ns, suffix) {
105
+ const { tenantId, appId } = ns;
106
+ if (!tenantId && !appId) {
107
+ return `${getStorageKeyPrefix()}${suffix}`;
108
+ }
109
+ const tid = tenantId || "";
110
+ const aid = appId || "";
111
+ return `auth_${tid}_${aid}_${suffix}`;
112
+ }
113
+ var TOKEN_KEY_SUFFIXES = {
114
+ accessToken: "access_token",
115
+ refreshToken: "refresh_token",
116
+ expiresAt: "expires_at"
117
+ };
118
+ var CONFIG_KEY_SUFFIXES = {
119
+ authServerUrl: "auth_server_url",
120
+ clientId: "client_id",
121
+ redirectUri: "redirect_uri",
122
+ tenantId: "tenant_id",
123
+ tenantKey: "tenant_key",
124
+ codeVerifier: "code_verifier",
125
+ state: "state",
126
+ callbackProcessed: "callback_processed",
127
+ callbackProcessing: "callback_processing",
128
+ callbackAttempted: "callback_attempted",
129
+ flowInProgress: "flow_in_progress",
130
+ flowTimestamp: "flow_timestamp",
131
+ logoutProcessing: "logout_processing",
132
+ propsConfig: "props_config",
133
+ propsChanged: "props_changed",
134
+ tenantSwitched: "tenant_switched",
135
+ newTenantKey: "new_tenant_key",
136
+ stateMismatch: "state_mismatch",
137
+ bootstrapInProgress: "bootstrap_in_progress",
138
+ bootstrapTimestamp: "bootstrap_timestamp",
139
+ callbackHandled: "callback_handled",
140
+ idToken: "id_token"
141
+ };
142
+
143
+ // src/infrastructure/storage/context.ts
144
+ var currentAdapter = null;
145
+ var currentNamespace = { tenantId: "", appId: "" };
146
+ function setStorageContext(adapter, namespace = {}) {
147
+ currentAdapter = adapter;
148
+ currentNamespace = {
149
+ tenantId: namespace.tenantId ?? "",
150
+ appId: namespace.appId ?? ""
151
+ };
152
+ }
153
+ function getStorageAdapter() {
154
+ const runtime = getAuthRuntime();
155
+ if (runtime?.storageAdapter) return runtime.storageAdapter;
156
+ return currentAdapter;
157
+ }
158
+ function getStorageNamespace() {
159
+ const runtime = getAuthRuntime();
160
+ if (runtime?.storageNamespace) return { ...runtime.storageNamespace };
161
+ return { ...currentNamespace };
162
+ }
163
+
164
+ // src/infrastructure/storage/memory-adapter.ts
165
+ function createMemoryStorageAdapter(namespace) {
166
+ const store = /* @__PURE__ */ new Map();
167
+ const getTokensSync = () => {
168
+ const accessToken = store.get(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.accessToken)) ?? null;
169
+ const refreshToken = store.get(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.refreshToken)) ?? null;
170
+ const expiresAtStr = store.get(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.expiresAt)) ?? null;
171
+ if (!accessToken || !refreshToken || !expiresAtStr) return null;
172
+ const expiresAt = parseInt(expiresAtStr, 10);
173
+ if (isNaN(expiresAt)) return null;
174
+ return { accessToken, refreshToken, expiresAt };
175
+ };
176
+ return {
177
+ async getTokens() {
178
+ return getTokensSync();
179
+ },
180
+ getTokensSync,
181
+ async setTokens(tokens) {
182
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.accessToken), tokens.accessToken);
183
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.refreshToken), tokens.refreshToken);
184
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.expiresAt), String(tokens.expiresAt));
185
+ },
186
+ setTokensSync(tokens) {
187
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.accessToken), tokens.accessToken);
188
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.refreshToken), tokens.refreshToken);
189
+ store.set(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.expiresAt), String(tokens.expiresAt));
190
+ },
191
+ async clearTokens() {
192
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.accessToken));
193
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.refreshToken));
194
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.expiresAt));
195
+ },
196
+ clearTokensSync() {
197
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.accessToken));
198
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.refreshToken));
199
+ store.delete(buildStorageKey(namespace, TOKEN_KEY_SUFFIXES.expiresAt));
200
+ },
201
+ async getItem(key) {
202
+ return store.get(key) ?? null;
203
+ },
204
+ getItemSync(key) {
205
+ return store.get(key) ?? null;
206
+ },
207
+ async setItem(key, value) {
208
+ store.set(key, value);
209
+ },
210
+ setItemSync(key, value) {
211
+ store.set(key, value);
212
+ },
213
+ async removeItem(key) {
214
+ store.delete(key);
215
+ },
216
+ removeItemSync(key) {
217
+ store.delete(key);
218
+ },
219
+ async clear() {
220
+ store.clear();
221
+ },
222
+ clearSync() {
223
+ store.clear();
224
+ }
225
+ };
226
+ }
227
+
228
+ // src/infrastructure/storage/session-adapter.ts
229
+ function getSessionStorage() {
230
+ if (typeof window === "undefined") return null;
231
+ try {
232
+ return window.sessionStorage;
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+ function createSecureSessionStorageAdapter(namespace) {
238
+ return {
239
+ async getTokens() {
240
+ return getTokensSyncImpl(namespace);
241
+ },
242
+ getTokensSync() {
243
+ return getTokensSyncImpl(namespace);
244
+ },
245
+ async setTokens(tokens) {
246
+ setTokensSyncImpl(namespace, tokens);
247
+ },
248
+ setTokensSync(tokens) {
249
+ setTokensSyncImpl(namespace, tokens);
250
+ },
251
+ async clearTokens() {
252
+ clearTokensSyncImpl(namespace);
253
+ },
254
+ clearTokensSync() {
255
+ clearTokensSyncImpl(namespace);
256
+ },
257
+ async getItem(key) {
258
+ return getItemSyncImpl(key);
259
+ },
260
+ getItemSync(key) {
261
+ return getItemSyncImpl(key);
262
+ },
263
+ async setItem(key, value) {
264
+ setItemSyncImpl(key, value);
265
+ },
266
+ setItemSync(key, value) {
267
+ setItemSyncImpl(key, value);
268
+ },
269
+ async removeItem(key) {
270
+ removeItemSyncImpl(key);
271
+ },
272
+ removeItemSync(key) {
273
+ removeItemSyncImpl(key);
274
+ },
275
+ async clear() {
276
+ clearSyncImpl(namespace);
277
+ },
278
+ clearSync() {
279
+ clearSyncImpl(namespace);
280
+ }
281
+ };
282
+ }
283
+ function getTokensSyncImpl(ns) {
284
+ const storage = getSessionStorage();
285
+ if (!storage) return null;
286
+ try {
287
+ const accessToken = storage.getItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.accessToken));
288
+ const refreshToken = storage.getItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.refreshToken));
289
+ const expiresAtStr = storage.getItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.expiresAt));
290
+ if (!accessToken || !refreshToken || !expiresAtStr) return null;
291
+ const expiresAt = parseInt(expiresAtStr, 10);
292
+ if (isNaN(expiresAt)) return null;
293
+ return { accessToken, refreshToken, expiresAt };
294
+ } catch {
295
+ return null;
296
+ }
297
+ }
298
+ function setTokensSyncImpl(ns, tokens) {
299
+ const storage = getSessionStorage();
300
+ if (!storage) return;
301
+ try {
302
+ storage.setItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.accessToken), tokens.accessToken);
303
+ storage.setItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.refreshToken), tokens.refreshToken);
304
+ storage.setItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.expiresAt), String(tokens.expiresAt));
305
+ } catch {
306
+ }
307
+ }
308
+ function clearTokensSyncImpl(ns) {
309
+ const storage = getSessionStorage();
310
+ if (!storage) return;
311
+ try {
312
+ storage.removeItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.accessToken));
313
+ storage.removeItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.refreshToken));
314
+ storage.removeItem(buildStorageKey(ns, TOKEN_KEY_SUFFIXES.expiresAt));
315
+ } catch {
316
+ }
317
+ }
318
+ function getItemSyncImpl(key) {
319
+ const storage = getSessionStorage();
320
+ if (!storage) return null;
321
+ try {
322
+ return storage.getItem(key);
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+ function setItemSyncImpl(key, value) {
328
+ const storage = getSessionStorage();
329
+ if (!storage) return;
330
+ try {
331
+ storage.setItem(key, value);
332
+ } catch {
333
+ }
334
+ }
335
+ function removeItemSyncImpl(key) {
336
+ const storage = getSessionStorage();
337
+ if (!storage) return;
338
+ try {
339
+ storage.removeItem(key);
340
+ } catch {
341
+ }
342
+ }
343
+ function clearSyncImpl(ns) {
344
+ const storage = getSessionStorage();
345
+ if (!storage) return;
346
+ try {
347
+ const prefix = ns.tenantId || ns.appId ? `auth_${ns.tenantId || ""}_${ns.appId || ""}_` : getStorageKeyPrefix();
348
+ const keys = [];
349
+ for (let i = 0; i < storage.length; i++) {
350
+ const key = storage.key(i);
351
+ if (key && key.startsWith(prefix)) keys.push(key);
352
+ }
353
+ keys.forEach((k) => storage.removeItem(k));
354
+ } catch {
355
+ }
356
+ }
357
+
358
+ // src/infrastructure/storage/cookie-adapter.ts
359
+ var DEFAULT_COOKIE_OPTIONS = {
360
+ path: "/",
361
+ sameSite: "lax",
362
+ maxAge: 60 * 60 * 24 * 7
363
+ // 7 days for refresh token
364
+ };
365
+ function getDocument() {
366
+ if (typeof document === "undefined") return null;
367
+ return document;
368
+ }
369
+ function getCookie(name) {
370
+ const doc = getDocument();
371
+ if (!doc || !doc.cookie) return null;
372
+ const match = doc.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
373
+ return match ? decodeURIComponent(match[2]) : null;
374
+ }
375
+ function setCookie(name, value, options = {}) {
376
+ const doc = getDocument();
377
+ if (!doc) return;
378
+ const { path = "/", sameSite = "lax", maxAge } = { ...DEFAULT_COOKIE_OPTIONS, ...options };
379
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)};path=${path};SameSite=${sameSite}`;
380
+ if (maxAge != null) cookie += `;max-age=${maxAge}`;
381
+ doc.cookie = cookie;
382
+ }
383
+ function deleteCookie(name) {
384
+ const doc = getDocument();
385
+ if (!doc) return;
386
+ doc.cookie = `${encodeURIComponent(name)}=;path=/;max-age=0`;
387
+ }
388
+ function createCookieStorageAdapter(namespace, options) {
389
+ const prefix = options?.cookiePrefix ?? "auth";
390
+ const keyPrefix = namespace.tenantId || namespace.appId ? `auth_${namespace.tenantId || ""}_${namespace.appId || ""}_` : getStorageKeyPrefix();
391
+ const cookieName = (suffix) => `${prefix}_${namespace.tenantId || "t"}_${namespace.appId || "a"}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
392
+ return {
393
+ async getTokens() {
394
+ const accessToken = getCookie(cookieName(TOKEN_KEY_SUFFIXES.accessToken));
395
+ const refreshToken = getCookie(cookieName(TOKEN_KEY_SUFFIXES.refreshToken));
396
+ const expiresAtStr = getCookie(cookieName(TOKEN_KEY_SUFFIXES.expiresAt));
397
+ if (!accessToken || !refreshToken || !expiresAtStr) return null;
398
+ const expiresAt = parseInt(expiresAtStr, 10);
399
+ if (isNaN(expiresAt)) return null;
400
+ return { accessToken, refreshToken, expiresAt };
401
+ },
402
+ async setTokens(tokens) {
403
+ setCookie(cookieName(TOKEN_KEY_SUFFIXES.accessToken), tokens.accessToken, { maxAge: 60 * 60 });
404
+ setCookie(cookieName(TOKEN_KEY_SUFFIXES.refreshToken), tokens.refreshToken, { maxAge: 60 * 60 * 24 * 7 });
405
+ setCookie(cookieName(TOKEN_KEY_SUFFIXES.expiresAt), String(tokens.expiresAt), { maxAge: 60 * 60 * 24 });
406
+ },
407
+ async clearTokens() {
408
+ deleteCookie(cookieName(TOKEN_KEY_SUFFIXES.accessToken));
409
+ deleteCookie(cookieName(TOKEN_KEY_SUFFIXES.refreshToken));
410
+ deleteCookie(cookieName(TOKEN_KEY_SUFFIXES.expiresAt));
411
+ },
412
+ async getItem(key) {
413
+ const name = key.startsWith(keyPrefix) ? key.slice(keyPrefix.length) : key;
414
+ return getCookie(cookieName(name));
415
+ },
416
+ async setItem(key, value) {
417
+ const name = key.startsWith(keyPrefix) ? key.slice(keyPrefix.length) : key;
418
+ setCookie(cookieName(name), value);
419
+ },
420
+ async removeItem(key) {
421
+ const name = key.startsWith(keyPrefix) ? key.slice(keyPrefix.length) : key;
422
+ deleteCookie(cookieName(name));
423
+ },
424
+ async clear() {
425
+ const suffixes = Object.values(TOKEN_KEY_SUFFIXES);
426
+ suffixes.forEach((s) => deleteCookie(cookieName(s)));
427
+ Object.values(CONFIG_KEY_SUFFIXES).forEach((suffix) => deleteCookie(cookieName(suffix)));
428
+ }
429
+ };
430
+ }
431
+
432
+ // src/infrastructure/storage/api.ts
433
+ var KEY_SUFFIXES = {
434
+ authServerUrl: "auth_server_url",
435
+ clientId: "client_id",
436
+ redirectUri: "redirect_uri",
437
+ tenantId: "tenant_id",
438
+ tenantKey: "tenant_key",
439
+ codeVerifier: "code_verifier",
440
+ state: "state",
441
+ accessToken: "access_token",
442
+ refreshToken: "refresh_token",
443
+ expiresAt: "expires_at"
444
+ };
445
+ var STORAGE_KEYS = {
446
+ get authServerUrl() {
447
+ return getStorageKey(KEY_SUFFIXES.authServerUrl);
448
+ },
449
+ get clientId() {
450
+ return getStorageKey(KEY_SUFFIXES.clientId);
451
+ },
452
+ get redirectUri() {
453
+ return getStorageKey(KEY_SUFFIXES.redirectUri);
454
+ },
455
+ get tenantId() {
456
+ return getStorageKey(KEY_SUFFIXES.tenantId);
457
+ },
458
+ get tenantKey() {
459
+ return getStorageKey(KEY_SUFFIXES.tenantKey);
460
+ },
461
+ get codeVerifier() {
462
+ return getStorageKey(KEY_SUFFIXES.codeVerifier);
463
+ },
464
+ get state() {
465
+ return getStorageKey(KEY_SUFFIXES.state);
466
+ },
467
+ get accessToken() {
468
+ return getStorageKey(KEY_SUFFIXES.accessToken);
469
+ },
470
+ get refreshToken() {
471
+ return getStorageKey(KEY_SUFFIXES.refreshToken);
472
+ },
473
+ get expiresAt() {
474
+ return getStorageKey(KEY_SUFFIXES.expiresAt);
475
+ }
476
+ };
477
+ function keyToSuffix(key) {
478
+ const prefix = getStorageKeyPrefix();
479
+ return key.startsWith(prefix) ? key.slice(prefix.length) : key;
480
+ }
481
+ function setStorage(key, value) {
482
+ const adapter = getStorageAdapter();
483
+ if (!adapter?.setItemSync) return;
484
+ const ns = getStorageNamespace();
485
+ const suffix = keyToSuffix(key);
486
+ try {
487
+ adapter.setItemSync(buildStorageKey(ns, suffix), value);
488
+ } catch {
489
+ }
490
+ }
491
+ function getStorage(key) {
492
+ const adapter = getStorageAdapter();
493
+ if (!adapter?.getItemSync) return null;
494
+ const ns = getStorageNamespace();
495
+ const suffix = keyToSuffix(key);
496
+ try {
497
+ return adapter.getItemSync(buildStorageKey(ns, suffix));
498
+ } catch {
499
+ return null;
500
+ }
501
+ }
502
+ function removeStorage(key) {
503
+ const adapter = getStorageAdapter();
504
+ if (!adapter?.removeItemSync) return;
505
+ const ns = getStorageNamespace();
506
+ const suffix = keyToSuffix(key);
507
+ try {
508
+ adapter.removeItemSync(buildStorageKey(ns, suffix));
509
+ } catch {
510
+ }
511
+ }
512
+ function clearAuthStorage() {
513
+ const adapter = getStorageAdapter();
514
+ if (!adapter?.clearSync) return;
515
+ try {
516
+ adapter.clearTokensSync?.();
517
+ adapter.clearSync();
518
+ } catch {
519
+ }
520
+ }
521
+ function clearAuthSessionStorage() {
522
+ const adapter = getStorageAdapter();
523
+ if (!adapter?.clearSync) return;
524
+ try {
525
+ adapter.clearTokensSync?.();
526
+ adapter.clearSync();
527
+ } catch {
528
+ }
529
+ }
530
+ function clearAllCookies() {
531
+ const runtime = getAuthRuntime();
532
+ try {
533
+ runtime?.clearCookies?.clearCookies();
534
+ } catch {
535
+ }
536
+ }
537
+ function clearAllStorage() {
538
+ const forceLogin = getStorage(getStorageKey("force_login"));
539
+ const logoutInProgress = getStorage(getStorageKey("logout_in_progress"));
540
+ const preservedTenantKey = getStorage(getStorageKey("preserved_tenant_key"));
541
+ clearAuthSessionStorage();
542
+ clearAuthStorage();
543
+ if (forceLogin === "true") setStorage(getStorageKey("force_login"), "true");
544
+ if (logoutInProgress === "true") setStorage(getStorageKey("logout_in_progress"), "true");
545
+ if (preservedTenantKey) setStorage(getStorageKey("preserved_tenant_key"), preservedTenantKey);
546
+ clearAllCookies();
547
+ }
548
+ function getAccessToken() {
549
+ const adapter = getStorageAdapter();
550
+ if (adapter?.getTokensSync) {
551
+ const t = adapter.getTokensSync();
552
+ return t?.accessToken ?? null;
553
+ }
554
+ return getStorage(STORAGE_KEYS.accessToken);
555
+ }
556
+ function getRefreshToken() {
557
+ const adapter = getStorageAdapter();
558
+ if (adapter?.getTokensSync) {
559
+ const t = adapter.getTokensSync();
560
+ return t?.refreshToken ?? null;
561
+ }
562
+ return getStorage(STORAGE_KEYS.refreshToken);
563
+ }
564
+ function getExpiresAt() {
565
+ const adapter = getStorageAdapter();
566
+ if (adapter?.getTokensSync) {
567
+ const t = adapter.getTokensSync();
568
+ return t?.expiresAt ?? null;
569
+ }
570
+ const expiresAt = getStorage(STORAGE_KEYS.expiresAt);
571
+ if (!expiresAt) return null;
572
+ const timestamp = parseInt(expiresAt, 10);
573
+ return isNaN(timestamp) ? null : timestamp;
574
+ }
575
+ function isAccessTokenExpired() {
576
+ const expiresAt = getExpiresAt();
577
+ if (!expiresAt) return true;
578
+ const buffer = 60 * 1e3;
579
+ return Date.now() >= expiresAt - buffer;
580
+ }
581
+ function getClientId() {
582
+ return getStorage(STORAGE_KEYS.clientId);
583
+ }
584
+ function getRedirectUri() {
585
+ return getStorage(STORAGE_KEYS.redirectUri);
586
+ }
587
+ function getTenantId() {
588
+ return getStorage(STORAGE_KEYS.tenantId);
589
+ }
590
+ function getCodeVerifier() {
591
+ return getStorage(STORAGE_KEYS.codeVerifier);
592
+ }
593
+ function getState() {
594
+ return getStorage(STORAGE_KEYS.state);
595
+ }
596
+ function getTenantKey() {
597
+ return getStorage(STORAGE_KEYS.tenantKey);
598
+ }
599
+ function setTenantKey(tenantKey) {
600
+ setStorage(STORAGE_KEYS.tenantKey, tenantKey);
601
+ }
602
+
603
+ // src/utils/logger.ts
604
+ function isDev() {
605
+ if (typeof window === "undefined") {
606
+ return false;
607
+ }
608
+ const hostname = window.location.hostname;
609
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.endsWith(".local");
610
+ }
611
+ var Logger = class {
612
+ constructor() {
613
+ this.prefix = "[tenneo-auth-gateway-plugin]";
614
+ }
615
+ log(level, message, data) {
616
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
617
+ const logMessage = `${this.prefix} [${timestamp}] [${level.toUpperCase()}] ${message}`;
618
+ switch (level) {
619
+ case "info":
620
+ console.log(logMessage, data || "");
621
+ break;
622
+ case "warn":
623
+ console.warn(logMessage, data || "");
624
+ break;
625
+ case "error":
626
+ console.error(logMessage, data || "");
627
+ break;
628
+ case "debug":
629
+ if (isDev()) {
630
+ console.debug(logMessage, data || "");
631
+ }
632
+ break;
633
+ }
634
+ }
635
+ info(message, data) {
636
+ this.log("info", message, data);
637
+ }
638
+ warn(message, data) {
639
+ this.log("warn", message, data);
640
+ }
641
+ error(message, data) {
642
+ this.log("error", message, data);
643
+ }
644
+ debug(message, data) {
645
+ this.log("debug", message, data);
646
+ }
647
+ };
648
+ var logger = new Logger();
649
+
650
+ // src/domain/errors.ts
651
+ function isLocalhostHostname(hostname) {
652
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.startsWith("192.168.") || hostname.startsWith("10.") || hostname.startsWith("172.16.");
653
+ }
654
+ function isDev2() {
655
+ if (typeof window === "undefined") return false;
656
+ return isLocalhostHostname(window.location.hostname);
657
+ }
658
+ function enforceHttps(allowLocalhost = true) {
659
+ if (typeof window === "undefined") return;
660
+ const isLocalhostWindow = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.startsWith("192.168.") || window.location.hostname.startsWith("10.") || window.location.hostname.startsWith("172.16.");
661
+ const isHttp = window.location.protocol === "http:";
662
+ if (allowLocalhost && isLocalhostWindow && isHttp) {
663
+ logger.debug("HTTP allowed on localhost for development");
664
+ return;
665
+ }
666
+ if (isHttp && !isLocalhostWindow) {
667
+ const error = new Error("HTTPS is required for authentication. Please use HTTPS to access this application.");
668
+ logger.error("HTTPS enforcement failed", {
669
+ protocol: window.location.protocol,
670
+ hostname: window.location.hostname
671
+ });
672
+ throw error;
673
+ }
674
+ logger.debug("HTTPS check passed", { protocol: window.location.protocol });
675
+ }
676
+ function sanitizeErrorMessage(error, genericMessage) {
677
+ if (isDev2()) {
678
+ if (error instanceof Error) return error.message;
679
+ return String(error);
680
+ }
681
+ return genericMessage;
682
+ }
683
+ var SanitizedError = class extends Error {
684
+ constructor(code, genericMessage, originalError) {
685
+ super(genericMessage);
686
+ this.name = "SanitizedError";
687
+ this.code = code;
688
+ if (isDev2() && originalError instanceof Error) {
689
+ this.originalMessage = originalError.message;
690
+ }
691
+ }
692
+ };
693
+
694
+ // src/domain/auth-lifecycle.ts
695
+ function resolveAuthLifecycleState(input) {
696
+ const {
697
+ url,
698
+ getStorage: getStorage3,
699
+ isCallbackUrl: isCallbackUrl2,
700
+ hasValidToken,
701
+ hasToken,
702
+ hasRefreshToken,
703
+ isTokenExpired
704
+ } = input;
705
+ const forceLogin = getStorage3("force_login") === "true";
706
+ const callbackProcessed = getStorage3("callback_processed") === "true";
707
+ const tenantSwitched = getStorage3("tenant_switched") === "true";
708
+ if (forceLogin) return "FORCE_LOGIN" /* FORCE_LOGIN */;
709
+ if (isCallbackUrl2(url)) return "PROCESSING_CALLBACK" /* PROCESSING_CALLBACK */;
710
+ if (hasValidToken) return "AUTHENTICATED" /* AUTHENTICATED */;
711
+ if (callbackProcessed && !hasToken) return "LOGIN_REQUIRED" /* LOGIN_REQUIRED */;
712
+ if (hasToken && isTokenExpired && hasRefreshToken) return "TOKEN_EXPIRED" /* TOKEN_EXPIRED */;
713
+ if (hasToken && isTokenExpired && !hasRefreshToken) return "LOGIN_REQUIRED" /* LOGIN_REQUIRED */;
714
+ if (tenantSwitched) return "TENANT_SWITCH" /* TENANT_SWITCH */;
715
+ return "LOGIN_REQUIRED" /* LOGIN_REQUIRED */;
716
+ }
717
+
718
+ // src/application/lifecycle-state.ts
719
+ var BOOTSTRAP_STALE_MS = 5e3;
720
+ var bootstrapInProgress = false;
721
+ var bootstrapTimestamp = 0;
722
+ var callbackProcessing = false;
723
+ var flowInProgress = false;
724
+ var flowTimestamp = 0;
725
+ function isBootstrapInProgress() {
726
+ if (!bootstrapInProgress) return false;
727
+ if (!bootstrapTimestamp) return true;
728
+ return Date.now() - bootstrapTimestamp < BOOTSTRAP_STALE_MS;
729
+ }
730
+ function setBootstrapInProgress() {
731
+ bootstrapInProgress = true;
732
+ bootstrapTimestamp = Date.now();
733
+ }
734
+ function clearBootstrapInProgress() {
735
+ bootstrapInProgress = false;
736
+ bootstrapTimestamp = 0;
737
+ }
738
+ function isCallbackProcessing() {
739
+ return callbackProcessing;
740
+ }
741
+ function setCallbackProcessing() {
742
+ callbackProcessing = true;
743
+ }
744
+ function clearCallbackProcessing() {
745
+ callbackProcessing = false;
746
+ }
747
+ function isFlowInProgress() {
748
+ return flowInProgress;
749
+ }
750
+ function setFlowInProgress() {
751
+ flowInProgress = true;
752
+ flowTimestamp = Date.now();
753
+ }
754
+ function clearFlowInProgress() {
755
+ flowInProgress = false;
756
+ flowTimestamp = 0;
757
+ }
758
+ function isFlowFlagStale(thresholdMs = 3e3) {
759
+ if (!flowTimestamp) return true;
760
+ return Date.now() - flowTimestamp > thresholdMs;
761
+ }
762
+ function clearFlowFlags() {
763
+ clearFlowInProgress();
764
+ }
765
+ function setFlowFlags() {
766
+ setFlowInProgress();
767
+ }
768
+
769
+ // src/strategies/oauth2-pkce/pkce.ts
770
+ function generateCodeVerifier(length = 128) {
771
+ const array = new Uint8Array(length);
772
+ crypto.getRandomValues(array);
773
+ const base64 = btoa(String.fromCharCode(...array));
774
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
775
+ }
776
+ async function generateCodeChallenge(verifier) {
777
+ try {
778
+ const encoder = new TextEncoder();
779
+ const data = encoder.encode(verifier);
780
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
781
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
782
+ const base64 = btoa(String.fromCharCode(...hashArray));
783
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
784
+ } catch {
785
+ throw new Error("Failed to generate code challenge");
786
+ }
787
+ }
788
+ function generateState() {
789
+ const array = new Uint8Array(32);
790
+ crypto.getRandomValues(array);
791
+ const base64 = btoa(String.fromCharCode(...array));
792
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
793
+ }
794
+
795
+ // src/config/env.ts
796
+ var ENV_KEYS = {
797
+ CLIENT_ID: ["VITE_CLIENT_ID", "CLIENT_ID"],
798
+ TENANT_ID: ["VITE_TENANT_ID", "TENANT_ID"],
799
+ CLIENTCODE: ["VITE_CLIENTCODE", "CLIENTCODE"]
800
+ };
801
+ function getEnvValue(keys) {
802
+ try {
803
+ const g = typeof globalThis !== "undefined" ? globalThis : null;
804
+ const env = g?.process && typeof g.process.env === "object" ? g.process.env : null;
805
+ if (env) {
806
+ for (const key of keys) {
807
+ const v = env[key];
808
+ if (v != null && String(v).trim()) return String(v).trim();
809
+ }
810
+ }
811
+ } catch {
812
+ }
813
+ try {
814
+ const meta = typeof import.meta !== "undefined" ? import.meta.env : void 0;
815
+ if (meta) {
816
+ for (const key of keys) {
817
+ const v = meta[key];
818
+ if (v != null && String(v).trim()) return String(v).trim();
819
+ }
820
+ }
821
+ } catch {
822
+ }
823
+ return "";
824
+ }
825
+ var FORBIDDEN_WINDOW_KEYS = ["apiBaseUrl", "apiTenantResolutionBaseUrl", "apiAuthBaseUrl", "authServerUrl", "defaultDomain", "API_BASE_URL", "AUTH_SERVER_URL", "DEFAULT_DOMAIN"];
826
+ function getWindowConfigRaw() {
827
+ if (typeof window === "undefined") return null;
828
+ const w = window;
829
+ const raw = w.__AUTH_CONFIG__ ?? w.__APP_CONFIG__;
830
+ if (!raw || typeof raw !== "object") return null;
831
+ for (const forbidden of FORBIDDEN_WINDOW_KEYS) {
832
+ if (forbidden in raw) {
833
+ throw new Error("Attempted to override protected internal configuration");
834
+ }
835
+ }
836
+ const out = {};
837
+ const map = {
838
+ clientId: "CLIENT_ID",
839
+ tenantId: "TENANT_ID",
840
+ clientcode: "CLIENTCODE"
841
+ };
842
+ for (const [winKey, sdkKey] of Object.entries(map)) {
843
+ const v = raw[winKey];
844
+ if (v != null && typeof v === "string" && v.trim()) out[sdkKey] = v.trim();
845
+ }
846
+ return Object.keys(out).length ? out : null;
847
+ }
848
+ function loadEnvConfig() {
849
+ const partial = {};
850
+ for (const [key, envKeys] of Object.entries(ENV_KEYS)) {
851
+ const v = getEnvValue(envKeys);
852
+ if (v) partial[key] = v;
853
+ }
854
+ return partial;
855
+ }
856
+ function loadWindowConfig() {
857
+ const raw = getWindowConfigRaw();
858
+ if (!raw) return null;
859
+ return raw;
860
+ }
861
+ var schema = z.object({
862
+ CLIENT_ID: z.string().min(1, "CLIENT_ID is required"),
863
+ TENANT_ID: z.string().min(1, "TENANT_ID is required"),
864
+ CLIENTCODE: z.string().min(1, "CLIENTCODE is required")
865
+ });
866
+ function validateConfig(data) {
867
+ return schema.parse(data);
868
+ }
869
+
870
+ // src/config/types.ts
871
+ var INTERNAL_CONFIG_KEYS = ["API_TENANT_RESOLUTION_BASE_URL", "API_AUTH_BASE_URL", "AUTH_SERVER_URL", "DEFAULT_DOMAIN"];
872
+ var RUNTIME_CONFIG_KEYS = ["CLIENT_ID", "TENANT_ID", "CLIENTCODE"];
873
+
874
+ // src/config/constants.ts
875
+ var OAUTH_CONSTANTS = {
876
+ SCOPES: "openid profile email",
877
+ RESPONSE_TYPE: "code",
878
+ CODE_CHALLENGE_METHOD: "S256",
879
+ TOKEN_GRANT_TYPE: "authorization_code",
880
+ REFRESH_GRANT_TYPE: "refresh_token"
881
+ };
882
+ var STORAGE_KEY_PREFIX = "tenneo_auth_";
883
+ var STORAGE_KEYS2 = {
884
+ authServerUrl: `${STORAGE_KEY_PREFIX}auth_server_url`,
885
+ clientId: `${STORAGE_KEY_PREFIX}client_id`,
886
+ redirectUri: `${STORAGE_KEY_PREFIX}redirect_uri`,
887
+ tenantId: `${STORAGE_KEY_PREFIX}tenant_id`,
888
+ codeVerifier: `${STORAGE_KEY_PREFIX}code_verifier`,
889
+ state: `${STORAGE_KEY_PREFIX}state`,
890
+ accessToken: `${STORAGE_KEY_PREFIX}access_token`,
891
+ refreshToken: `${STORAGE_KEY_PREFIX}refresh_token`,
892
+ expiresAt: `${STORAGE_KEY_PREFIX}expires_at`
893
+ };
894
+ OAUTH_CONSTANTS.SCOPES;
895
+ var DEFAULT_RESPONSE_TYPE = OAUTH_CONSTANTS.RESPONSE_TYPE;
896
+ var DEFAULT_CODE_CHALLENGE_METHOD = OAUTH_CONSTANTS.CODE_CHALLENGE_METHOD;
897
+ var DEFAULT_TOKEN_GRANT_TYPE = OAUTH_CONSTANTS.TOKEN_GRANT_TYPE;
898
+ var DEFAULT_REFRESH_GRANT_TYPE = OAUTH_CONSTANTS.REFRESH_GRANT_TYPE;
899
+
900
+ // src/config/index.ts
901
+ var INTERNAL_DEFAULTS = {
902
+ API_TENANT_RESOLUTION_BASE_URL: "https://localhost:5020/api/v1",
903
+ API_AUTH_BASE_URL: "https://localhost:5001",
904
+ AUTH_SERVER_URL: "https://localhost:5001",
905
+ DEFAULT_DOMAIN: "https://localhost:5001"
906
+ };
907
+ function envOr(key) {
908
+ const v = getEnvValue([`VITE_${key}`, key]);
909
+ return v && v.trim() ? v.trim() : INTERNAL_DEFAULTS[key];
910
+ }
911
+ var INTERNAL_CONFIG = Object.freeze({
912
+ API_TENANT_RESOLUTION_BASE_URL: envOr("API_TENANT_RESOLUTION_BASE_URL"),
913
+ API_AUTH_BASE_URL: envOr("API_AUTH_BASE_URL"),
914
+ AUTH_SERVER_URL: envOr("AUTH_SERVER_URL"),
915
+ DEFAULT_DOMAIN: envOr("DEFAULT_DOMAIN")
916
+ });
917
+ var RUNTIME_CONFIG_DEFAULTS = Object.freeze({
918
+ CLIENT_ID: "tenneo",
919
+ TENANT_ID: "00000000-0000-0000-0000-000000000001",
920
+ CLIENTCODE: "productlms"
921
+ });
922
+ function pickRuntimeConfig(source) {
923
+ if (!source) return {};
924
+ const picked = {};
925
+ for (const key of RUNTIME_CONFIG_KEYS) {
926
+ const value = source[key];
927
+ if (value != null && typeof value === "string") {
928
+ const trimmed = value.trim();
929
+ if (trimmed) picked[key] = trimmed;
930
+ }
931
+ }
932
+ return picked;
933
+ }
934
+ ({
935
+ ...INTERNAL_CONFIG,
936
+ ...RUNTIME_CONFIG_DEFAULTS
937
+ });
938
+ var runtimeOverrides = {};
939
+ var runtimeConfigOverrides = {};
940
+ function mergeRuntime(base, ...sources) {
941
+ let out = { ...base };
942
+ for (const src of sources) {
943
+ if (!src) continue;
944
+ for (const key of Object.keys(src)) {
945
+ const value = src[key];
946
+ if (value != null && String(value).trim() !== "") out[key] = String(value).trim();
947
+ }
948
+ }
949
+ return out;
950
+ }
951
+ function assertNoInternalOverrides(source) {
952
+ if (!source) return;
953
+ for (const key of INTERNAL_CONFIG_KEYS) {
954
+ if (source[key] !== void 0) throw new Error("Attempted to override protected internal configuration");
955
+ }
956
+ }
957
+ var INTERNAL_KEY_SET = new Set(INTERNAL_CONFIG_KEYS);
958
+ var HOST_TO_RUNTIME_KEYS = {
959
+ clientId: "CLIENT_ID",
960
+ tenantId: "TENANT_ID",
961
+ clientcode: "CLIENTCODE"
962
+ };
963
+ function sanitizeRuntimeOverrides(source) {
964
+ if (!source) return void 0;
965
+ const filtered = {};
966
+ for (const [key, raw] of Object.entries(source)) {
967
+ if (raw == null) continue;
968
+ if (INTERNAL_KEY_SET.has(key)) continue;
969
+ const trimmed = String(raw).trim();
970
+ if (!trimmed) continue;
971
+ const sdkKey = HOST_TO_RUNTIME_KEYS[key] ?? key;
972
+ filtered[sdkKey] = trimmed;
973
+ }
974
+ const picked = pickRuntimeConfig(filtered);
975
+ return Object.keys(picked).length ? picked : void 0;
976
+ }
977
+ function resolveConfig(overrides) {
978
+ assertNoInternalOverrides(runtimeOverrides);
979
+ assertNoInternalOverrides(overrides);
980
+ const windowCfg = loadWindowConfig();
981
+ const envCfg = loadEnvConfig();
982
+ const mergedRuntime = mergeRuntime(
983
+ RUNTIME_CONFIG_DEFAULTS,
984
+ envCfg,
985
+ windowCfg ?? void 0,
986
+ runtimeOverrides,
987
+ sanitizeRuntimeOverrides(overrides)
988
+ );
989
+ const validatedRuntime = validateConfig(mergedRuntime);
990
+ return { ...INTERNAL_CONFIG, ...validatedRuntime };
991
+ }
992
+ function getConfig() {
993
+ return resolveConfig();
994
+ }
995
+ function setConfig(overrides) {
996
+ assertNoInternalOverrides(overrides);
997
+ const sanitized = sanitizeRuntimeOverrides(overrides);
998
+ if (sanitized) runtimeOverrides = { ...runtimeOverrides, ...sanitized };
999
+ const { basePath, redirectUri } = overrides;
1000
+ if (basePath != null && basePath.trim() !== "") runtimeConfigOverrides = { ...runtimeConfigOverrides, basePath: basePath.trim() };
1001
+ if (redirectUri != null && redirectUri.trim() !== "") runtimeConfigOverrides = { ...runtimeConfigOverrides, redirectUri: redirectUri.trim() };
1002
+ }
1003
+ function clearConfigOverrides() {
1004
+ runtimeOverrides = {};
1005
+ runtimeConfigOverrides = {};
1006
+ }
1007
+ function getWindowBasePath() {
1008
+ if (typeof window === "undefined") return "";
1009
+ const w = window;
1010
+ const raw = w.__AUTH_CONFIG__ ?? w.__APP_CONFIG__;
1011
+ const bp = raw?.basePath;
1012
+ if (bp != null && typeof bp === "string" && bp.trim()) return bp.trim();
1013
+ const pathname = window.location.pathname;
1014
+ const segments = pathname.split("/").filter(Boolean);
1015
+ const commonRoutes = ["dashboards", "callback", "confirmation", "client-confirmed", "client-provisioned", "approval-completed"];
1016
+ if (segments.length > 0 && !commonRoutes.includes(segments[0])) {
1017
+ let base = "/" + segments[0];
1018
+ if (base.endsWith("/")) base = base.slice(0, -1);
1019
+ return base;
1020
+ }
1021
+ return "";
1022
+ }
1023
+ function getBasePath() {
1024
+ if (runtimeConfigOverrides.basePath != null && runtimeConfigOverrides.basePath !== "") return runtimeConfigOverrides.basePath;
1025
+ return getWindowBasePath();
1026
+ }
1027
+ function getRedirectUri2() {
1028
+ if (typeof window !== "undefined") {
1029
+ return `${window.location.origin}/callback`;
1030
+ }
1031
+ return getEnvValue(["VITE_REDIRECT_URI", "REDIRECT_URI"]) || "http://localhost:5173/callback";
1032
+ }
1033
+ function hasRuntimeConfig() {
1034
+ return loadWindowConfig() !== null || Object.keys(runtimeOverrides).length > 0;
1035
+ }
1036
+ var Config = {
1037
+ getApiTenantResolutionBaseUrl() {
1038
+ return getConfig().API_TENANT_RESOLUTION_BASE_URL;
1039
+ },
1040
+ getApiAuthBaseUrl() {
1041
+ return getConfig().API_AUTH_BASE_URL;
1042
+ },
1043
+ getAuthServerUrl() {
1044
+ return getConfig().AUTH_SERVER_URL;
1045
+ },
1046
+ getDefaultDomain() {
1047
+ return getConfig().DEFAULT_DOMAIN;
1048
+ },
1049
+ getClientId() {
1050
+ return getConfig().CLIENT_ID;
1051
+ },
1052
+ getTenantId() {
1053
+ return getConfig().TENANT_ID;
1054
+ },
1055
+ getClientCode() {
1056
+ return getConfig().CLIENTCODE;
1057
+ },
1058
+ getRedirectUri() {
1059
+ return getRedirectUri2();
1060
+ },
1061
+ getBasePath() {
1062
+ return getBasePath();
1063
+ },
1064
+ hasRuntimeConfig() {
1065
+ return hasRuntimeConfig();
1066
+ },
1067
+ getAll() {
1068
+ const c = getConfig();
1069
+ return {
1070
+ apiTenantResolutionBaseUrl: c.API_TENANT_RESOLUTION_BASE_URL,
1071
+ apiAuthBaseUrl: c.API_AUTH_BASE_URL,
1072
+ authServerUrl: c.AUTH_SERVER_URL,
1073
+ defaultDomain: c.DEFAULT_DOMAIN,
1074
+ clientId: c.CLIENT_ID,
1075
+ tenantId: c.TENANT_ID,
1076
+ clientCode: c.CLIENTCODE,
1077
+ redirectUri: getRedirectUri2(),
1078
+ basePath: getBasePath()
1079
+ };
1080
+ }
1081
+ };
1082
+ ({
1083
+ AUTH_SERVER_URL: Config.getAuthServerUrl(),
1084
+ CLIENT_ID: Config.getClientId(),
1085
+ TENANT_ID: Config.getTenantId(),
1086
+ CLIENTCODE: Config.getClientCode()
1087
+ });
1088
+ function getEnvConfig() {
1089
+ const authServerUrl = Config.getAuthServerUrl();
1090
+ const clientId = Config.getClientId();
1091
+ const tenantId = Config.getTenantId();
1092
+ const redirectUri = Config.getRedirectUri();
1093
+ try {
1094
+ new URL(authServerUrl);
1095
+ new URL(redirectUri);
1096
+ } catch {
1097
+ return Promise.resolve(null);
1098
+ }
1099
+ return Promise.resolve({
1100
+ authServerUrl: authServerUrl.trim(),
1101
+ clientId: clientId.trim(),
1102
+ redirectUri: redirectUri.trim(),
1103
+ tenantId: tenantId.trim()
1104
+ });
1105
+ }
1106
+ function getEnvConfigSync() {
1107
+ const authServerUrl = Config.getAuthServerUrl();
1108
+ const clientId = Config.getClientId();
1109
+ const tenantId = Config.getTenantId();
1110
+ const redirectUri = Config.getRedirectUri();
1111
+ try {
1112
+ new URL(authServerUrl);
1113
+ new URL(redirectUri);
1114
+ } catch {
1115
+ return null;
1116
+ }
1117
+ return {
1118
+ authServerUrl: authServerUrl.trim(),
1119
+ clientId: clientId.trim(),
1120
+ redirectUri: redirectUri.trim(),
1121
+ tenantId: tenantId.trim()
1122
+ };
1123
+ }
1124
+ function hasEnvFallback() {
1125
+ return getEnvConfigSync() !== null;
1126
+ }
1127
+
1128
+ // src/utils/validation.ts
1129
+ function isValidGuid(guid) {
1130
+ if (!guid || typeof guid !== "string") {
1131
+ return false;
1132
+ }
1133
+ const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1134
+ return guidRegex.test(guid.trim());
1135
+ }
1136
+ function validateTenantId(tenantId) {
1137
+ if (!tenantId || tenantId.trim() === "") {
1138
+ return {
1139
+ isValid: false,
1140
+ error: "Tenant ID is required"
1141
+ };
1142
+ }
1143
+ if (!isValidGuid(tenantId)) {
1144
+ return {
1145
+ isValid: false,
1146
+ error: "Tenant ID must be a valid GUID format (00000000-0000-0000-0000-000000000001)"
1147
+ };
1148
+ }
1149
+ return { isValid: true };
1150
+ }
1151
+
1152
+ // src/application/domain-facade.ts
1153
+ function isLocalhost2() {
1154
+ const runtime = getAuthRuntime();
1155
+ const hostname = runtime ? runtime.urlProvider.getCurrentUrl().hostname : typeof window !== "undefined" ? window.location.hostname : "";
1156
+ return isLocalhost(hostname);
1157
+ }
1158
+ function extractTenantKey() {
1159
+ const runtime = getAuthRuntime();
1160
+ if (runtime) {
1161
+ const url = runtime.urlProvider.getCurrentUrl();
1162
+ return extractTenantKeyFromUrl(url, runtime.configProvider, getStorage, getStorageKey);
1163
+ }
1164
+ return extractTenantKeyLegacy();
1165
+ }
1166
+ function extractTenantKeyLegacy() {
1167
+ if (typeof window === "undefined") return Config.getClientCode();
1168
+ try {
1169
+ const propsConfigStr = getStorage(getStorageKey("props_config"));
1170
+ if (propsConfigStr) {
1171
+ const propsConfig = JSON.parse(propsConfigStr);
1172
+ if (propsConfig.tenantKey?.trim()) return propsConfig.tenantKey.trim();
1173
+ }
1174
+ const stored = getStorage(getStorageKey("tenant_key"));
1175
+ if (stored?.trim()) return stored.trim();
1176
+ const urlParams = new URLSearchParams(window.location.search);
1177
+ const t = urlParams.get("t");
1178
+ if (t?.trim()) return t.trim();
1179
+ const hostname = window.location.hostname;
1180
+ const parts = hostname.split(".");
1181
+ if (parts.length >= 2 && parts[0] && parts[0].toLowerCase() !== "productlms") return parts[0].trim();
1182
+ return Config.getClientCode();
1183
+ } catch {
1184
+ return Config.getClientCode();
1185
+ }
1186
+ }
1187
+ function isCallbackUrl() {
1188
+ const runtime = getAuthRuntime();
1189
+ if (runtime) return isCallbackUrlFromUrl(runtime.urlProvider.getCurrentUrl());
1190
+ if (typeof window === "undefined") return false;
1191
+ const params = new URLSearchParams(window.location.search);
1192
+ return params.has("code") && params.has("state");
1193
+ }
1194
+
1195
+ // src/application/events/AuthEvents.ts
1196
+ var AuthEventNames = {
1197
+ LOGIN_STARTED: "LOGIN_STARTED",
1198
+ LOGIN_SUCCESS: "LOGIN_SUCCESS",
1199
+ LOGIN_FAILED: "LOGIN_FAILED",
1200
+ CALLBACK_SUCCESS: "CALLBACK_SUCCESS",
1201
+ CALLBACK_FAILED: "CALLBACK_FAILED",
1202
+ TOKEN_REFRESH_SUCCESS: "TOKEN_REFRESH_SUCCESS",
1203
+ TOKEN_REFRESH_FAILED: "TOKEN_REFRESH_FAILED",
1204
+ TENANT_SWITCHED: "TENANT_SWITCHED",
1205
+ LOGOUT_COMPLETED: "LOGOUT_COMPLETED",
1206
+ BOOTSTRAP_STATE_DETECTED: "BOOTSTRAP_STATE_DETECTED",
1207
+ REDIRECT_LOOP_RISK: "REDIRECT_LOOP_RISK"
1208
+ };
1209
+ var listeners = /* @__PURE__ */ new Map();
1210
+ function getHandlers(event) {
1211
+ let set = listeners.get(event);
1212
+ if (!set) {
1213
+ set = /* @__PURE__ */ new Set();
1214
+ listeners.set(event, set);
1215
+ }
1216
+ return set;
1217
+ }
1218
+ function getWildcardHandlers() {
1219
+ let set = listeners.get("*");
1220
+ if (!set) {
1221
+ set = /* @__PURE__ */ new Set();
1222
+ listeners.set("*", set);
1223
+ }
1224
+ return set;
1225
+ }
1226
+ function emitAuthEvent(event, payload) {
1227
+ const handlers = getHandlers(event);
1228
+ const wildcard = getWildcardHandlers();
1229
+ const p = payload ?? {};
1230
+ handlers.forEach((fn) => {
1231
+ try {
1232
+ fn(p);
1233
+ } catch {
1234
+ }
1235
+ });
1236
+ wildcard.forEach((fn) => {
1237
+ try {
1238
+ fn({ ...p, event });
1239
+ } catch {
1240
+ }
1241
+ });
1242
+ }
1243
+ function subscribeAuthEvent(event, handler) {
1244
+ const set = event === "*" ? getWildcardHandlers() : getHandlers(event);
1245
+ set.add(handler);
1246
+ return () => set.delete(handler);
1247
+ }
1248
+
1249
+ // src/application/security/redirectLoopGuard.ts
1250
+ var DEFAULT_MAX_REDIRECTS = 5;
1251
+ var WINDOW_MS = 60 * 1e3;
1252
+ var redirectCount = 0;
1253
+ var firstRedirectAt = 0;
1254
+ function resetRedirectLoopGuard() {
1255
+ redirectCount = 0;
1256
+ firstRedirectAt = 0;
1257
+ }
1258
+ function checkAndIncrementRedirect(maxAttempts = DEFAULT_MAX_REDIRECTS) {
1259
+ const now = Date.now();
1260
+ if (now - firstRedirectAt > WINDOW_MS) {
1261
+ redirectCount = 0;
1262
+ firstRedirectAt = now;
1263
+ }
1264
+ if (redirectCount >= maxAttempts) {
1265
+ emitAuthEvent(AuthEventNames.REDIRECT_LOOP_RISK, {
1266
+ message: `Redirect attempt limit (${maxAttempts}) exceeded`
1267
+ });
1268
+ return false;
1269
+ }
1270
+ redirectCount++;
1271
+ return true;
1272
+ }
1273
+
1274
+ // src/strategies/oauth2-pkce/oauth.ts
1275
+ function storeTenantConfig(config) {
1276
+ const validation = validateTenantId(config.tenantId);
1277
+ if (!validation.isValid) {
1278
+ logger.warn("Tenant ID validation failed", { tenantId: config.tenantId });
1279
+ }
1280
+ setStorage(STORAGE_KEYS.authServerUrl, config.authServer);
1281
+ setStorage(STORAGE_KEYS.clientId, config.client.clientId);
1282
+ setStorage(STORAGE_KEYS.redirectUri, Config.getRedirectUri());
1283
+ setStorage(STORAGE_KEYS.tenantId, config.tenantId);
1284
+ if (config.tenantKey) {
1285
+ setStorage(STORAGE_KEYS.tenantKey, config.tenantKey);
1286
+ }
1287
+ }
1288
+ async function initiateAuthFlow(config) {
1289
+ try {
1290
+ logger.info("Initiating OAuth authorization flow");
1291
+ const token = getAccessToken();
1292
+ if (token && !isAccessTokenExpired()) {
1293
+ logger.debug("Valid token already exists, skipping auth flow");
1294
+ clearFlowFlags();
1295
+ return;
1296
+ }
1297
+ if (isCallbackUrl()) {
1298
+ logger.debug("Callback URL detected - clearing flow flag to allow callback processing");
1299
+ clearFlowFlags();
1300
+ return;
1301
+ }
1302
+ if (isFlowInProgress()) {
1303
+ if (isFlowFlagStale(3e3)) {
1304
+ logger.info("Stale auth flow flag detected, clearing and proceeding");
1305
+ clearFlowFlags();
1306
+ } else {
1307
+ logger.debug("Auth flow already in progress (recent), skipping duplicate redirect");
1308
+ return;
1309
+ }
1310
+ }
1311
+ setFlowFlags();
1312
+ if (!config?.authServer || !config.client?.clientId || !config.client?.redirectUri) {
1313
+ throw new Error("Invalid tenant configuration for auth flow");
1314
+ }
1315
+ storeTenantConfig(config);
1316
+ const codeVerifier = generateCodeVerifier();
1317
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
1318
+ const state = generateState();
1319
+ removeStorage(STORAGE_KEYS.codeVerifier);
1320
+ removeStorage(STORAGE_KEYS.state);
1321
+ setStorage(STORAGE_KEYS.codeVerifier, codeVerifier);
1322
+ setStorage(STORAGE_KEYS.state, state);
1323
+ const authUrl = buildAuthorizationUrl(config, codeChallenge, state);
1324
+ if (!authUrl || !(authUrl.startsWith("http://") || authUrl.startsWith("https://"))) {
1325
+ throw new Error("Invalid authorization URL generated");
1326
+ }
1327
+ if (!checkAndIncrementRedirect()) {
1328
+ logger.warn("Redirect loop protection: max redirect attempts exceeded");
1329
+ clearFlowFlags();
1330
+ return;
1331
+ }
1332
+ logger.info("Redirecting to authorization server");
1333
+ emitAuthEvent(AuthEventNames.LOGIN_STARTED, { tenantKey: config.tenantKey });
1334
+ const runtime = getAuthRuntime();
1335
+ if (runtime) runtime.urlProvider.redirect(authUrl);
1336
+ else if (typeof window !== "undefined") window.location.replace(authUrl);
1337
+ } catch (error) {
1338
+ clearFlowFlags();
1339
+ logger.error("Failed to initiate auth flow", {
1340
+ error: error instanceof Error ? error.message : String(error)
1341
+ });
1342
+ throw error;
1343
+ }
1344
+ }
1345
+ function buildAuthorizationUrl(config, codeChallenge, state) {
1346
+ if (isLocalhost2()) {
1347
+ logger.debug("Building authorization URL", {
1348
+ authServer: config.authServer,
1349
+ clientId: config.client.clientId,
1350
+ redirectUri: config.client.redirectUri,
1351
+ scopes: config.client.scopes
1352
+ });
1353
+ }
1354
+ const storedTenantId = getStorage(STORAGE_KEYS.tenantId);
1355
+ const storedClientId = getStorage(STORAGE_KEYS.clientId);
1356
+ const tenantIdToUse = storedTenantId || config.tenantId;
1357
+ const clientIdToUse = storedClientId || config.client.clientId;
1358
+ const redirectUriToUse = Config.getRedirectUri();
1359
+ const params = new URLSearchParams({
1360
+ client_id: `public-${clientIdToUse}`,
1361
+ tenant_id: tenantIdToUse,
1362
+ redirect_uri: redirectUriToUse,
1363
+ response_type: DEFAULT_RESPONSE_TYPE,
1364
+ scope: config.client.scopes,
1365
+ state,
1366
+ code_challenge: codeChallenge,
1367
+ code_challenge_method: DEFAULT_CODE_CHALLENGE_METHOD
1368
+ });
1369
+ return `${config.authServer}/connect/authorize?${params.toString()}`;
1370
+ }
1371
+
1372
+ // src/strategies/oauth2-pkce/token-exchange.ts
1373
+ function storeTokens(tokenData) {
1374
+ const expiresIn = tokenData.expires_in || 3600;
1375
+ const expiresAt = Date.now() + expiresIn * 1e3;
1376
+ const adapter = getStorageAdapter();
1377
+ if (adapter?.setTokensSync) {
1378
+ adapter.setTokensSync({
1379
+ accessToken: tokenData.access_token,
1380
+ refreshToken: tokenData.refresh_token,
1381
+ expiresAt
1382
+ });
1383
+ }
1384
+ setStorage(STORAGE_KEYS.accessToken, tokenData.access_token);
1385
+ setStorage(STORAGE_KEYS.refreshToken, tokenData.refresh_token);
1386
+ setStorage(STORAGE_KEYS.expiresAt, expiresAt.toString());
1387
+ if (isLocalhost2()) {
1388
+ logger.debug("Tokens stored successfully", {
1389
+ expiresAt: new Date(expiresAt).toISOString(),
1390
+ expiresIn
1391
+ });
1392
+ } else {
1393
+ logger.info("Tokens stored successfully");
1394
+ }
1395
+ }
1396
+ async function exchangeCodeForTokens(params) {
1397
+ try {
1398
+ const { code, codeVerifier, clientId, redirectUri } = params;
1399
+ const apiAuthBaseUrl = Config.getApiAuthBaseUrl();
1400
+ const tokenUrl = `${apiAuthBaseUrl}/oauth/token`;
1401
+ const body = new URLSearchParams({
1402
+ grant_type: DEFAULT_TOKEN_GRANT_TYPE,
1403
+ code,
1404
+ client_id: `public-${clientId}`,
1405
+ redirect_uri: redirectUri,
1406
+ code_verifier: codeVerifier
1407
+ });
1408
+ const response = await fetch(tokenUrl, {
1409
+ method: "POST",
1410
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1411
+ body: body.toString()
1412
+ });
1413
+ if (!response.ok) {
1414
+ const responseData = await response.json().catch(() => ({}));
1415
+ logger.error("Token exchange failed", {
1416
+ status: response.status,
1417
+ statusText: response.statusText,
1418
+ responseData,
1419
+ requestUrl: tokenUrl
1420
+ });
1421
+ throw new Error(
1422
+ `Token exchange failed: ${response.status} ${response.statusText}${Object.keys(responseData).length ? ` - ${JSON.stringify(responseData)}` : ""}`
1423
+ );
1424
+ }
1425
+ const tokenData = await response.json();
1426
+ if (!tokenData.access_token || !tokenData.refresh_token || !tokenData.expires_in) {
1427
+ logger.error("Invalid token response received");
1428
+ throw new Error("Invalid token response");
1429
+ }
1430
+ storeTokens(tokenData);
1431
+ return tokenData;
1432
+ } catch (error) {
1433
+ const isAborted = error instanceof Error && error.name === "AbortError";
1434
+ if (isAborted) {
1435
+ logger.warn("Token exchange request canceled (likely due to navigation or component unmount)");
1436
+ const existingToken = getAccessToken();
1437
+ if (existingToken && !isAccessTokenExpired()) {
1438
+ return {
1439
+ access_token: existingToken,
1440
+ refresh_token: getRefreshToken() || "",
1441
+ expires_in: 3600
1442
+ };
1443
+ }
1444
+ throw new Error("Token exchange request was canceled");
1445
+ }
1446
+ logger.error("Token exchange failed", {
1447
+ message: error instanceof Error ? error.message : String(error)
1448
+ });
1449
+ throw error;
1450
+ }
1451
+ }
1452
+ var refreshPromise = null;
1453
+ async function refreshAccessToken() {
1454
+ if (refreshPromise) return refreshPromise;
1455
+ refreshPromise = (async () => {
1456
+ try {
1457
+ const refreshToken = getRefreshToken();
1458
+ const clientId = getClientId();
1459
+ const tenantId = getTenantId();
1460
+ if (!refreshToken || !clientId || !tenantId) {
1461
+ logger.error("Token refresh failed: missing required data");
1462
+ throw new Error("Cannot refresh token: missing required data");
1463
+ }
1464
+ const apiAuthBaseUrl = Config.getApiAuthBaseUrl();
1465
+ const tokenUrl = `${apiAuthBaseUrl}/oauth/refresh`;
1466
+ const body = new URLSearchParams({
1467
+ grant_type: DEFAULT_REFRESH_GRANT_TYPE,
1468
+ refresh_token: refreshToken,
1469
+ client_id: clientId.startsWith("public-") ? clientId : `public-${clientId}`
1470
+ });
1471
+ const response = await fetch(tokenUrl, {
1472
+ method: "POST",
1473
+ headers: {
1474
+ "Content-Type": "application/x-www-form-urlencoded",
1475
+ "__tenant": tenantId,
1476
+ "Accept": "*/*"
1477
+ },
1478
+ body: body.toString()
1479
+ });
1480
+ if (!response.ok) {
1481
+ const responseData = await response.json().catch(() => ({}));
1482
+ const isExpiredOrInvalidGrant = response.status === 401 || responseData?.error === "invalid_grant" || typeof responseData?.error_description === "string" && (responseData.error_description.includes("expired") || responseData.error_description.includes("invalid"));
1483
+ if (isExpiredOrInvalidGrant) {
1484
+ removeStorage(STORAGE_KEYS.refreshToken);
1485
+ removeStorage(STORAGE_KEYS.accessToken);
1486
+ removeStorage(STORAGE_KEYS.expiresAt);
1487
+ emitAuthEvent(AuthEventNames.TOKEN_REFRESH_FAILED, {
1488
+ error: "Refresh token expired or invalid",
1489
+ message: String(responseData?.error_description ?? responseData?.error ?? "Token refresh failed")
1490
+ });
1491
+ }
1492
+ logger.error("Token refresh failed", {
1493
+ status: response.status,
1494
+ statusText: response.statusText,
1495
+ responseData,
1496
+ requestUrl: tokenUrl
1497
+ });
1498
+ throw new Error(
1499
+ `Token refresh failed: ${response.status} ${response.statusText}${Object.keys(responseData).length ? ` - ${JSON.stringify(responseData)}` : ""}`
1500
+ );
1501
+ }
1502
+ const tokenData = await response.json();
1503
+ if (!tokenData.access_token || !tokenData.refresh_token) {
1504
+ logger.error("Invalid refresh token response received");
1505
+ throw new Error("Invalid refresh token response");
1506
+ }
1507
+ storeTokens(tokenData);
1508
+ emitAuthEvent(AuthEventNames.TOKEN_REFRESH_SUCCESS);
1509
+ return tokenData.access_token;
1510
+ } catch (error) {
1511
+ if (error instanceof Error) {
1512
+ logger.error("Token refresh failed", { message: error.message });
1513
+ emitAuthEvent(AuthEventNames.TOKEN_REFRESH_FAILED, { error: error.message });
1514
+ }
1515
+ throw error;
1516
+ } finally {
1517
+ refreshPromise = null;
1518
+ }
1519
+ })();
1520
+ return refreshPromise;
1521
+ }
1522
+ function isProcessed() {
1523
+ return getStorage(getStorageKey("callback_processed")) === "true";
1524
+ }
1525
+ function isProcessing() {
1526
+ return isCallbackProcessing();
1527
+ }
1528
+ function markProcessing() {
1529
+ setCallbackProcessing();
1530
+ }
1531
+ function markProcessed() {
1532
+ setStorage(getStorageKey("callback_processed"), "true");
1533
+ clearCallbackProcessing();
1534
+ }
1535
+ function clearProcessing() {
1536
+ clearCallbackProcessing();
1537
+ removeStorage(getStorageKey("callback_processed"));
1538
+ }
1539
+ var urlCleaned = false;
1540
+ function cleanUrl() {
1541
+ if (urlCleaned) return;
1542
+ if (isProcessing()) return;
1543
+ const runtime = getAuthRuntime();
1544
+ const clean = runtime ? `${runtime.urlProvider.getCurrentUrl().origin}${runtime.urlProvider.getCurrentUrl().pathname}` : typeof window !== "undefined" ? `${window.location.origin}${window.location.pathname}` : "/";
1545
+ if (runtime) runtime.urlProvider.redirect(clean);
1546
+ else if (typeof window !== "undefined") window.location.replace(clean);
1547
+ urlCleaned = true;
1548
+ }
1549
+ function cleanUrlSoft() {
1550
+ if (urlCleaned) return;
1551
+ if (isProcessing()) return;
1552
+ const runtime = getAuthRuntime();
1553
+ const clean = runtime ? `${runtime.urlProvider.getCurrentUrl().origin}${runtime.urlProvider.getCurrentUrl().pathname}` : typeof window !== "undefined" ? `${window.location.origin}${window.location.pathname}` : "/";
1554
+ if (runtime) runtime.urlProvider.replaceState(clean);
1555
+ else if (typeof window !== "undefined") window.history.replaceState({}, typeof document !== "undefined" ? document.title : "", clean);
1556
+ urlCleaned = true;
1557
+ }
1558
+ function clearAllAuthFlags() {
1559
+ removeStorage(getStorageKey("force_login"));
1560
+ removeStorage(getStorageKey("logout_in_progress"));
1561
+ clearFlowFlags();
1562
+ }
1563
+ async function handleCallback() {
1564
+ if (isProcessed()) {
1565
+ logger.debug("Callback already processed in this session - preventing duplicate processing");
1566
+ if (isCallbackUrl()) {
1567
+ if (!isProcessing()) cleanUrlSoft();
1568
+ }
1569
+ clearFlowFlags();
1570
+ return true;
1571
+ }
1572
+ if (isProcessing()) {
1573
+ logger.debug("Callback processing already in progress - waiting for completion");
1574
+ return true;
1575
+ }
1576
+ if (getStorage(getStorageKey("force_login")) === "true") {
1577
+ logger.info("Force-login flag present \u2192 callback skipped (logout occurred)");
1578
+ const runtime = getAuthRuntime();
1579
+ if (runtime && runtime.urlProvider.getCurrentUrl().search) cleanUrlSoft();
1580
+ else if (typeof window !== "undefined" && window.location.search) cleanUrlSoft();
1581
+ clearAllAuthFlags();
1582
+ clearProcessing();
1583
+ return false;
1584
+ }
1585
+ const storedTenantKey = getTenantKey();
1586
+ let currentTenantKey = null;
1587
+ try {
1588
+ const propsConfigStr = getStorage(getStorageKey("props_config"));
1589
+ if (propsConfigStr) {
1590
+ const propsConfig = JSON.parse(propsConfigStr);
1591
+ currentTenantKey = propsConfig.tenantKey || null;
1592
+ }
1593
+ } catch {
1594
+ }
1595
+ if (!currentTenantKey) currentTenantKey = storedTenantKey;
1596
+ if (currentTenantKey && storedTenantKey && currentTenantKey !== storedTenantKey) {
1597
+ logger.info("TenantKey changed - bypassing callback, starting fresh login");
1598
+ clearAllStorage();
1599
+ clearAllAuthFlags();
1600
+ clearProcessing();
1601
+ setStorage(getStorageKey("tenant_switched"), "true");
1602
+ setStorage(getStorageKey("new_tenant_key"), currentTenantKey);
1603
+ setTenantKey(currentTenantKey);
1604
+ if (isCallbackUrl()) cleanUrlSoft();
1605
+ return false;
1606
+ }
1607
+ if (!isCallbackUrl()) return false;
1608
+ markProcessing();
1609
+ clearFlowFlags();
1610
+ urlCleaned = false;
1611
+ try {
1612
+ const search = getAuthRuntime()?.urlProvider.getCurrentUrl().search ?? (typeof window !== "undefined" ? window.location.search : "");
1613
+ const params = new URLSearchParams(search);
1614
+ const code = params.get("code");
1615
+ const state = params.get("state");
1616
+ const error = params.get("error");
1617
+ if (error || !code || !state) {
1618
+ logger.error("Invalid OAuth callback params", { error, code, state });
1619
+ clearAllAuthFlags();
1620
+ clearProcessing();
1621
+ markProcessed();
1622
+ cleanUrlSoft();
1623
+ return false;
1624
+ }
1625
+ const storedState = getState();
1626
+ if (!storedState || storedState !== state) {
1627
+ const currentTenantKeyFromUrl = extractTenantKey();
1628
+ let storedTenantKeyFromProps = null;
1629
+ try {
1630
+ const propsConfigStr = getStorage(getStorageKey("props_config"));
1631
+ if (propsConfigStr) {
1632
+ const propsConfig = JSON.parse(propsConfigStr);
1633
+ storedTenantKeyFromProps = propsConfig.tenantKey || null;
1634
+ }
1635
+ } catch {
1636
+ }
1637
+ const tenantKeyChanged = currentTenantKeyFromUrl && storedTenantKeyFromProps && currentTenantKeyFromUrl !== storedTenantKeyFromProps;
1638
+ if (tenantKeyChanged) {
1639
+ logger.info("OAuth state mismatch due to tenantKey change - expected behavior, starting fresh login");
1640
+ } else {
1641
+ logger.error("OAuth state mismatch - redirecting to login");
1642
+ }
1643
+ clearAllAuthFlags();
1644
+ clearProcessing();
1645
+ markProcessed();
1646
+ cleanUrlSoft();
1647
+ setStorage(getStorageKey("state_mismatch"), "true");
1648
+ return false;
1649
+ }
1650
+ const codeVerifier = getCodeVerifier();
1651
+ if (!codeVerifier) {
1652
+ logger.error("Missing PKCE verifier");
1653
+ clearAllAuthFlags();
1654
+ clearProcessing();
1655
+ markProcessed();
1656
+ cleanUrlSoft();
1657
+ return false;
1658
+ }
1659
+ const clientId = getClientId();
1660
+ const redirectUri = getRedirectUri();
1661
+ const tenantId = getTenantId();
1662
+ if (!clientId || !redirectUri || !tenantId) {
1663
+ logger.error("Missing OAuth config during callback");
1664
+ clearAllAuthFlags();
1665
+ clearProcessing();
1666
+ markProcessed();
1667
+ cleanUrlSoft();
1668
+ return false;
1669
+ }
1670
+ const existingToken = getAccessToken();
1671
+ if (existingToken && !isAccessTokenExpired()) {
1672
+ logger.debug("Valid token already exists - skipping code exchange");
1673
+ removeStorage(STORAGE_KEYS.codeVerifier);
1674
+ removeStorage(STORAGE_KEYS.state);
1675
+ clearAllAuthFlags();
1676
+ markProcessed();
1677
+ cleanUrl();
1678
+ resetRedirectLoopGuard();
1679
+ emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
1680
+ return true;
1681
+ }
1682
+ const tokenData = await exchangeCodeForTokens({
1683
+ code,
1684
+ codeVerifier,
1685
+ clientId,
1686
+ redirectUri,
1687
+ tenantId
1688
+ });
1689
+ if (!tokenData?.access_token) {
1690
+ logger.error("Token exchange returned no access token");
1691
+ clearProcessing();
1692
+ clearAllAuthFlags();
1693
+ cleanUrlSoft();
1694
+ emitAuthEvent(AuthEventNames.CALLBACK_FAILED, { message: "No access token returned" });
1695
+ return false;
1696
+ }
1697
+ removeStorage(STORAGE_KEYS.codeVerifier);
1698
+ removeStorage(STORAGE_KEYS.state);
1699
+ clearAllAuthFlags();
1700
+ markProcessed();
1701
+ cleanUrl();
1702
+ logger.info("OAuth callback processed successfully");
1703
+ resetRedirectLoopGuard();
1704
+ emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
1705
+ return true;
1706
+ } catch (err) {
1707
+ if (axios.isAxiosError(err)) {
1708
+ const status = err.response?.status;
1709
+ const responseData = err.response?.data;
1710
+ const errorCode = responseData?.error;
1711
+ const errorDescription = responseData?.error_description || responseData?.details || "";
1712
+ if (status === 400 && errorCode === "invalid_grant" && (String(errorDescription).includes("already used") || String(errorDescription).includes("Code not found") || String(errorDescription).includes("expired, already used"))) {
1713
+ const existingToken2 = getAccessToken();
1714
+ const tokenValid2 = existingToken2 && !isAccessTokenExpired();
1715
+ if (tokenValid2) {
1716
+ removeStorage(STORAGE_KEYS.codeVerifier);
1717
+ removeStorage(STORAGE_KEYS.state);
1718
+ clearAllAuthFlags();
1719
+ markProcessed();
1720
+ cleanUrlSoft();
1721
+ logger.info("OAuth callback processed successfully (code already used, but token exists)");
1722
+ resetRedirectLoopGuard();
1723
+ emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
1724
+ return true;
1725
+ }
1726
+ }
1727
+ emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
1728
+ error: err.message,
1729
+ message: responseData?.error_description ?? err.message
1730
+ });
1731
+ logger.error("OAuth callback failed (Axios error)", {
1732
+ status,
1733
+ statusText: err.response?.statusText,
1734
+ responseData: err.response?.data,
1735
+ message: err.message,
1736
+ url: err.config?.url
1737
+ });
1738
+ } else {
1739
+ logger.error("OAuth callback failed", {
1740
+ error: err instanceof Error ? err.message : String(err)
1741
+ });
1742
+ }
1743
+ clearAllAuthFlags();
1744
+ const existingToken = getAccessToken();
1745
+ const tokenValid = existingToken && !isAccessTokenExpired();
1746
+ if (tokenValid) {
1747
+ markProcessed();
1748
+ cleanUrlSoft();
1749
+ resetRedirectLoopGuard();
1750
+ emitAuthEvent(AuthEventNames.CALLBACK_SUCCESS);
1751
+ return true;
1752
+ }
1753
+ emitAuthEvent(AuthEventNames.CALLBACK_FAILED, {
1754
+ error: err instanceof Error ? err.message : String(err)
1755
+ });
1756
+ clearProcessing();
1757
+ if (isCallbackUrl()) cleanUrlSoft();
1758
+ return false;
1759
+ }
1760
+ }
1761
+
1762
+ // src/infrastructure/tenant/TenantConfigCache.ts
1763
+ var DEFAULT_TTL_MS = 5 * 60 * 1e3;
1764
+ var TenantConfigCache = class {
1765
+ constructor(ttlMs = DEFAULT_TTL_MS) {
1766
+ this.cache = /* @__PURE__ */ new Map();
1767
+ this.inFlight = /* @__PURE__ */ new Map();
1768
+ this.ttlMs = ttlMs;
1769
+ }
1770
+ get(tenantKey) {
1771
+ const entry = this.cache.get(tenantKey);
1772
+ if (!entry) return null;
1773
+ if (Date.now() >= entry.expiresAt) {
1774
+ this.cache.delete(tenantKey);
1775
+ return null;
1776
+ }
1777
+ return entry.config;
1778
+ }
1779
+ set(tenantKey, config) {
1780
+ this.cache.set(tenantKey, {
1781
+ config,
1782
+ expiresAt: Date.now() + this.ttlMs
1783
+ });
1784
+ }
1785
+ invalidate(tenantKey) {
1786
+ if (tenantKey) {
1787
+ this.cache.delete(tenantKey);
1788
+ this.inFlight.delete(tenantKey);
1789
+ } else {
1790
+ this.cache.clear();
1791
+ this.inFlight.clear();
1792
+ }
1793
+ }
1794
+ async getOrResolve(tenantKey, resolver) {
1795
+ if (!tenantKey || !tenantKey.trim()) return null;
1796
+ const key = tenantKey.trim();
1797
+ const cached = this.get(key);
1798
+ if (cached) {
1799
+ logger.debug("Tenant config from cache", { tenantKey: key });
1800
+ return cached;
1801
+ }
1802
+ const existing = this.inFlight.get(key);
1803
+ if (existing) {
1804
+ logger.debug("Tenant resolution reused (in-flight)", { tenantKey: key });
1805
+ return existing;
1806
+ }
1807
+ const promise = (async () => {
1808
+ try {
1809
+ const config = await resolver(key);
1810
+ if (config) this.set(key, config);
1811
+ return config;
1812
+ } finally {
1813
+ this.inFlight.delete(key);
1814
+ }
1815
+ })();
1816
+ this.inFlight.set(key, promise);
1817
+ return promise;
1818
+ }
1819
+ };
1820
+
1821
+ // src/infrastructure/tenant/tenantResolution.ts
1822
+ var defaultTenantConfigCache = new TenantConfigCache();
1823
+ function getTenantKeyFromRuntime(url, config) {
1824
+ try {
1825
+ const propsConfigStr = getStorage(getStorageKey("props_config"));
1826
+ if (propsConfigStr) {
1827
+ const propsConfig = JSON.parse(propsConfigStr);
1828
+ if (propsConfig.tenantKey?.trim()) return propsConfig.tenantKey.trim();
1829
+ return null;
1830
+ }
1831
+ } catch {
1832
+ }
1833
+ return extractTenantKeyFromUrl(url, config, getStorage, getStorageKey);
1834
+ }
1835
+ function getPropsConfig() {
1836
+ try {
1837
+ const propsConfigStr = getStorage(getStorageKey("props_config"));
1838
+ if (!propsConfigStr) return null;
1839
+ const parsed = JSON.parse(propsConfigStr);
1840
+ if (parsed && typeof parsed === "object" && "authServerUrl" in parsed) {
1841
+ delete parsed.authServerUrl;
1842
+ }
1843
+ return parsed;
1844
+ } catch {
1845
+ removeStorage(getStorageKey("props_config"));
1846
+ return null;
1847
+ }
1848
+ }
1849
+ function mergeConfigWithProps(apiEnvConfig, propsConfig, tenantKey) {
1850
+ return {
1851
+ tenantId: propsConfig.tenantId || apiEnvConfig.tenantId,
1852
+ authServer: apiEnvConfig.authServer,
1853
+ tenantKey: tenantKey || apiEnvConfig.tenantKey,
1854
+ client: {
1855
+ clientId: propsConfig.clientId || apiEnvConfig.client.clientId,
1856
+ redirectUri: apiEnvConfig.client.redirectUri,
1857
+ scopes: apiEnvConfig.client.scopes
1858
+ }
1859
+ };
1860
+ }
1861
+ async function buildTenantConfigFromEnv() {
1862
+ const envConfig = await getEnvConfig();
1863
+ if (!envConfig) return null;
1864
+ return {
1865
+ tenantId: envConfig.tenantId,
1866
+ authServer: envConfig.authServerUrl,
1867
+ client: {
1868
+ clientId: envConfig.clientId,
1869
+ redirectUri: envConfig.redirectUri,
1870
+ scopes: "openid profile email"
1871
+ }
1872
+ };
1873
+ }
1874
+ async function resolveTenantConfig(tenantKey, tenantResolver) {
1875
+ if (!tenantKey) return null;
1876
+ const propsConfig = getPropsConfig();
1877
+ let apiEnvConfig = null;
1878
+ let apiResolved = false;
1879
+ apiEnvConfig = await tenantResolver.resolve(tenantKey);
1880
+ apiResolved = !!apiEnvConfig;
1881
+ if (!apiEnvConfig) {
1882
+ apiEnvConfig = await buildTenantConfigFromEnv();
1883
+ }
1884
+ if (!apiEnvConfig) return null;
1885
+ if (!apiResolved && propsConfig) {
1886
+ const mergedConfig = mergeConfigWithProps(apiEnvConfig, propsConfig, tenantKey);
1887
+ removeStorage(getStorageKey("props_config"));
1888
+ logger.info("Merged props config with ENV fallback config");
1889
+ return mergedConfig;
1890
+ }
1891
+ removeStorage(getStorageKey("props_config"));
1892
+ return {
1893
+ ...apiEnvConfig,
1894
+ tenantKey: tenantKey || apiEnvConfig.tenantKey
1895
+ };
1896
+ }
1897
+ async function resolveTenantConfigCached(tenantKey, tenantResolver) {
1898
+ if (!tenantKey) return null;
1899
+ return defaultTenantConfigCache.getOrResolve(
1900
+ tenantKey,
1901
+ (key) => resolveTenantConfig(key, tenantResolver)
1902
+ );
1903
+ }
1904
+
1905
+ // src/application/flows/ForceLoginFlow.ts
1906
+ var ForceLoginFlow = class {
1907
+ async execute(context) {
1908
+ const { runtime } = context;
1909
+ const { urlProvider, logger: log } = runtime;
1910
+ const getUrl = () => urlProvider.getCurrentUrl();
1911
+ if (isCallbackUrlFromUrl(getUrl())) {
1912
+ await handleCallback();
1913
+ if (isCallbackUrlFromUrl(getUrl())) {
1914
+ log.warn("Callback URL still present after cleanup, forcing URL clean");
1915
+ const u = getUrl();
1916
+ urlProvider.redirect(`${u.origin}${u.pathname}`);
1917
+ return { completed: true };
1918
+ }
1919
+ }
1920
+ clearFlowFlags();
1921
+ const tenantKey = getTenantKeyFromRuntime(getUrl(), runtime.configProvider);
1922
+ if (!tenantKey) return { completed: true };
1923
+ const tenantConfig = await resolveTenantConfigCached(tenantKey, runtime.tenantResolver);
1924
+ if (!tenantConfig) {
1925
+ log.error("Tenant config resolution failed for force login", { tenantKey });
1926
+ throw new SanitizedError(
1927
+ "TENANT_CONFIG_RESOLUTION_FAILED",
1928
+ "Unable to resolve tenant configuration",
1929
+ new Error(`Tenant config resolution failed for tenantKey: ${tenantKey}`)
1930
+ );
1931
+ }
1932
+ storeTenantConfig(tenantConfig);
1933
+ await initiateAuthFlow(tenantConfig);
1934
+ return { completed: true };
1935
+ }
1936
+ };
1937
+
1938
+ // src/application/flows/CallbackFlow.ts
1939
+ var CallbackFlow = class {
1940
+ async execute(context) {
1941
+ const { runtime } = context;
1942
+ const { urlProvider, logger: log } = runtime;
1943
+ const getUrl = () => urlProvider.getCurrentUrl();
1944
+ clearFlowFlags();
1945
+ const handled = await handleCallback();
1946
+ if (handled) {
1947
+ if (isCallbackUrlFromUrl(getUrl())) {
1948
+ if (isCallbackProcessing()) return { completed: true };
1949
+ const u = getUrl();
1950
+ urlProvider.redirect(`${u.origin}${u.pathname}`);
1951
+ return { completed: true };
1952
+ }
1953
+ return { completed: true };
1954
+ }
1955
+ if (isCallbackUrlFromUrl(getUrl())) {
1956
+ const u = getUrl();
1957
+ urlProvider.redirect(`${u.origin}${u.pathname}`);
1958
+ }
1959
+ return { completed: true };
1960
+ }
1961
+ };
1962
+
1963
+ // src/application/flows/AuthenticatedFlow.ts
1964
+ var AuthenticatedFlow = class {
1965
+ async execute(_context) {
1966
+ return { completed: true };
1967
+ }
1968
+ };
1969
+
1970
+ // src/application/engine/defaultEngine.ts
1971
+ var defaultEngine = null;
1972
+ function getDefaultAuthEngine() {
1973
+ return defaultEngine;
1974
+ }
1975
+ function setDefaultAuthEngine(engine) {
1976
+ defaultEngine = engine;
1977
+ }
1978
+
1979
+ // src/application/token.ts
1980
+ async function ensureValidAccessToken() {
1981
+ const engine = getDefaultAuthEngine();
1982
+ if (engine) {
1983
+ try {
1984
+ return await engine.getAccessToken();
1985
+ } catch {
1986
+ }
1987
+ }
1988
+ const token = getAccessToken();
1989
+ if (token && !isAccessTokenExpired()) return token;
1990
+ const refreshToken = getRefreshToken();
1991
+ if (!refreshToken) {
1992
+ logger.debug("No refresh token - cannot refresh; login required");
1993
+ throw new Error("No refresh token; login required");
1994
+ }
1995
+ return await refreshAccessToken();
1996
+ }
1997
+ function getAccessToken2() {
1998
+ return getAccessToken();
1999
+ }
2000
+
2001
+ // src/application/flows/RefreshFlow.ts
2002
+ var RefreshFlow = class {
2003
+ async execute(context) {
2004
+ const { runtime } = context;
2005
+ const { logger: log } = runtime;
2006
+ const getUrl = () => runtime.urlProvider.getCurrentUrl();
2007
+ log.debug("Token expired, attempting to refresh...");
2008
+ try {
2009
+ const refreshedToken = await ensureValidAccessToken();
2010
+ if (refreshedToken) {
2011
+ logger.debug("Token refreshed successfully, bootstrap complete");
2012
+ return { completed: true };
2013
+ }
2014
+ } catch (error) {
2015
+ logger.warn("Token refresh failed, will trigger new auth flow", {
2016
+ error: error instanceof Error ? error.message : String(error)
2017
+ });
2018
+ }
2019
+ const tenantKey = getTenantKeyFromRuntime(getUrl(), runtime.configProvider);
2020
+ if (!tenantKey) {
2021
+ throw new SanitizedError(
2022
+ "TENANT_KEY_RESOLUTION_FAILED",
2023
+ "Unable to resolve tenant",
2024
+ new Error("Tenant key missing")
2025
+ );
2026
+ }
2027
+ const tenantConfig = await resolveTenantConfigCached(tenantKey, runtime.tenantResolver);
2028
+ if (!tenantConfig) {
2029
+ throw new SanitizedError(
2030
+ "TENANT_CONFIG_RESOLUTION_FAILED",
2031
+ "Unable to resolve tenant configuration",
2032
+ new Error(`Tenant config resolution failed for tenantKey: ${tenantKey}`)
2033
+ );
2034
+ }
2035
+ setTenantKey(tenantKey);
2036
+ storeTenantConfig(tenantConfig);
2037
+ await initiateAuthFlow(tenantConfig);
2038
+ return { completed: true };
2039
+ }
2040
+ };
2041
+
2042
+ // src/application/flows/TenantSwitchFlow.ts
2043
+ var TenantSwitchFlow = class {
2044
+ async execute(context) {
2045
+ const { runtime } = context;
2046
+ const { urlProvider, logger: log } = runtime;
2047
+ const getUrl = () => urlProvider.getCurrentUrl();
2048
+ const newTenantKey = getStorage(getStorageKey("new_tenant_key"));
2049
+ log.info("Tenant switch detected - starting fresh login", { newTenantKey });
2050
+ emitAuthEvent(AuthEventNames.TENANT_SWITCHED, { tenantKey: newTenantKey ?? void 0 });
2051
+ removeStorage(getStorageKey("tenant_switched"));
2052
+ removeStorage(getStorageKey("new_tenant_key"));
2053
+ const tenantKey = newTenantKey || getTenantKeyFromRuntime(getUrl(), runtime.configProvider);
2054
+ if (!tenantKey) {
2055
+ throw new SanitizedError(
2056
+ "TENANT_KEY_RESOLUTION_FAILED",
2057
+ "Unable to resolve tenant after switch",
2058
+ new Error("Tenant key missing after switch")
2059
+ );
2060
+ }
2061
+ const tenantConfig = await resolveTenantConfigCached(tenantKey, runtime.tenantResolver);
2062
+ if (!tenantConfig) {
2063
+ throw new SanitizedError(
2064
+ "TENANT_CONFIG_RESOLUTION_FAILED",
2065
+ "Unable to resolve tenant configuration after switch",
2066
+ new Error(`Tenant config resolution failed for tenantKey: ${tenantKey}`)
2067
+ );
2068
+ }
2069
+ setTenantKey(tenantKey);
2070
+ storeTenantConfig(tenantConfig);
2071
+ await initiateAuthFlow(tenantConfig);
2072
+ return { completed: true };
2073
+ }
2074
+ };
2075
+
2076
+ // src/application/flows/LoginFlow.ts
2077
+ var LoginFlow = class {
2078
+ async execute(context) {
2079
+ const { runtime } = context;
2080
+ const { logger: log } = runtime;
2081
+ const getUrl = () => runtime.urlProvider.getCurrentUrl();
2082
+ const callbackProcessed = getStorage(getStorageKey("callback_processed")) === "true";
2083
+ const token = getAccessToken();
2084
+ if (callbackProcessed && !token) {
2085
+ log.warn("Callback processed but token still not available - clearing flag and allowing fresh login");
2086
+ removeStorage(getStorageKey("callback_processed"));
2087
+ clearCallbackProcessing();
2088
+ }
2089
+ const wasStateMismatch = getStorage(getStorageKey("state_mismatch")) === "true";
2090
+ if (wasStateMismatch) removeStorage(getStorageKey("state_mismatch"));
2091
+ const tenantKey = getTenantKeyFromRuntime(getUrl(), runtime.configProvider);
2092
+ if (!tenantKey) {
2093
+ throw new SanitizedError(
2094
+ "TENANT_KEY_RESOLUTION_FAILED",
2095
+ "Unable to resolve tenant",
2096
+ new Error("Tenant key missing")
2097
+ );
2098
+ }
2099
+ const tenantConfig = await resolveTenantConfigCached(tenantKey, runtime.tenantResolver);
2100
+ if (!tenantConfig) {
2101
+ throw new SanitizedError(
2102
+ "TENANT_CONFIG_RESOLUTION_FAILED",
2103
+ "Unable to resolve tenant configuration",
2104
+ new Error(`Tenant config resolution failed for tenantKey: ${tenantKey}`)
2105
+ );
2106
+ }
2107
+ setTenantKey(tenantKey);
2108
+ storeTenantConfig(tenantConfig);
2109
+ await initiateAuthFlow(tenantConfig);
2110
+ return { completed: true };
2111
+ }
2112
+ };
2113
+
2114
+ // src/application/flows/getFlowForState.ts
2115
+ var forceLoginFlow = new ForceLoginFlow();
2116
+ var callbackFlow = new CallbackFlow();
2117
+ var authenticatedFlow = new AuthenticatedFlow();
2118
+ var refreshFlow = new RefreshFlow();
2119
+ var tenantSwitchFlow = new TenantSwitchFlow();
2120
+ var loginFlow = new LoginFlow();
2121
+ function getFlowForState(state) {
2122
+ switch (state) {
2123
+ case "FORCE_LOGIN" /* FORCE_LOGIN */:
2124
+ return forceLoginFlow;
2125
+ case "PROCESSING_CALLBACK" /* PROCESSING_CALLBACK */:
2126
+ return callbackFlow;
2127
+ case "AUTHENTICATED" /* AUTHENTICATED */:
2128
+ return authenticatedFlow;
2129
+ case "TOKEN_EXPIRED" /* TOKEN_EXPIRED */:
2130
+ return refreshFlow;
2131
+ case "TENANT_SWITCH" /* TENANT_SWITCH */:
2132
+ return tenantSwitchFlow;
2133
+ case "LOGIN_REQUIRED" /* LOGIN_REQUIRED */:
2134
+ case "INITIALIZING" /* INITIALIZING */:
2135
+ case "LOGGED_OUT" /* LOGGED_OUT */:
2136
+ case "ERROR" /* ERROR */:
2137
+ default:
2138
+ return loginFlow;
2139
+ }
2140
+ }
2141
+
2142
+ // src/application/run-bootstrap.ts
2143
+ function createStorageReader() {
2144
+ return (suffix) => getStorage(getStorageKey(suffix));
2145
+ }
2146
+ async function runBootstrap() {
2147
+ const runtime = getAuthRuntime();
2148
+ if (!runtime) {
2149
+ logger.warn(
2150
+ "Auth runtime not set; skipping bootstrap. Set runtime via AuthProvider or setAuthRuntime()."
2151
+ );
2152
+ return;
2153
+ }
2154
+ if (isBootstrapInProgress()) {
2155
+ logger.debug("Bootstrap already in progress - skipping duplicate run");
2156
+ return;
2157
+ }
2158
+ setBootstrapInProgress();
2159
+ try {
2160
+ enforceHttps(true);
2161
+ const url = runtime.urlProvider.getCurrentUrl();
2162
+ const token = getAccessToken();
2163
+ const expired = isAccessTokenExpired();
2164
+ const hasValidToken = !!(token && !expired);
2165
+ const hasToken = !!token;
2166
+ const hasRefreshToken = !!getRefreshToken();
2167
+ const state = resolveAuthLifecycleState({
2168
+ url,
2169
+ getStorage: createStorageReader(),
2170
+ isCallbackUrl: isCallbackUrlFromUrl,
2171
+ hasValidToken,
2172
+ hasToken,
2173
+ hasRefreshToken,
2174
+ isTokenExpired: expired
2175
+ });
2176
+ emitAuthEvent(AuthEventNames.BOOTSTRAP_STATE_DETECTED, { state });
2177
+ const flow = getFlowForState(state);
2178
+ const context = { runtime, url, state };
2179
+ await flow.execute(context);
2180
+ } catch (error) {
2181
+ if (error instanceof SanitizedError) throw error;
2182
+ const r = getAuthRuntime();
2183
+ (r?.logger ?? logger).error("Bootstrap failed", {
2184
+ error: error instanceof Error ? error.message : String(error)
2185
+ });
2186
+ throw new Error(
2187
+ sanitizeErrorMessage(error, "Authentication initialization failed")
2188
+ );
2189
+ } finally {
2190
+ clearBootstrapInProgress();
2191
+ }
2192
+ }
2193
+
2194
+ // src/infrastructure/watchers/callbacks.ts
2195
+ var sessionClearedCallback = null;
2196
+ function setSessionClearedCallback(cb) {
2197
+ sessionClearedCallback = cb;
2198
+ }
2199
+ function getSessionClearedCallback() {
2200
+ return sessionClearedCallback;
2201
+ }
2202
+
2203
+ // src/application/bootstrap.ts
2204
+ async function bootstrapImpl() {
2205
+ const engine = getDefaultAuthEngine();
2206
+ if (engine) {
2207
+ await engine.init();
2208
+ return;
2209
+ }
2210
+ await runBootstrap();
2211
+ }
2212
+ setSessionClearedCallback(() => bootstrapImpl());
2213
+ async function bootstrap() {
2214
+ return bootstrapImpl();
2215
+ }
2216
+
2217
+ // src/strategies/cookie/cookie-auth.ts
2218
+ var COOKIE_NAMES = {
2219
+ accessToken: "tenneo_auth_access_token",
2220
+ refreshToken: "tenneo_auth_refresh_token"
2221
+ };
2222
+
2223
+ // src/infrastructure/watchers/cookie-watcher.ts
2224
+ var isWatching = false;
2225
+ var lastCookieSnapshot = "";
2226
+ var lastCookieMap = /* @__PURE__ */ new Map();
2227
+ var watchInterval = null;
2228
+ function getCookieSnapshot() {
2229
+ try {
2230
+ return document.cookie || "";
2231
+ } catch {
2232
+ return "";
2233
+ }
2234
+ }
2235
+ function getCookieNames() {
2236
+ try {
2237
+ const cookies = document.cookie.split(";");
2238
+ return cookies.map((cookie) => {
2239
+ const eqPos = cookie.indexOf("=");
2240
+ return eqPos > -1 ? cookie.substring(0, eqPos).trim() : cookie.trim();
2241
+ }).filter((name) => name.length > 0);
2242
+ } catch {
2243
+ return [];
2244
+ }
2245
+ }
2246
+ function getCookieMap() {
2247
+ const cookieMap = /* @__PURE__ */ new Map();
2248
+ try {
2249
+ const cookies = document.cookie.split(";");
2250
+ for (const cookie of cookies) {
2251
+ const eqPos = cookie.indexOf("=");
2252
+ if (eqPos > -1) {
2253
+ const name = cookie.substring(0, eqPos).trim();
2254
+ const value = cookie.substring(eqPos + 1).trim();
2255
+ if (name.length > 0) cookieMap.set(name, value);
2256
+ }
2257
+ }
2258
+ } catch {
2259
+ }
2260
+ return cookieMap;
2261
+ }
2262
+ function checkCookiesDeleted() {
2263
+ try {
2264
+ const currentSnapshot = getCookieSnapshot();
2265
+ const currentCookieMap = getCookieMap();
2266
+ const currentCookies = getCookieNames();
2267
+ if (lastCookieSnapshot && !currentSnapshot) {
2268
+ logger.info("All cookies deleted detected");
2269
+ return true;
2270
+ }
2271
+ if (lastCookieMap.size > 0) {
2272
+ const authCookieNames = [
2273
+ COOKIE_NAMES.accessToken,
2274
+ COOKIE_NAMES.refreshToken,
2275
+ "tenneo_auth",
2276
+ "tenneo_auth_access_token",
2277
+ "tenneo_auth_refresh_token",
2278
+ "auth",
2279
+ "session",
2280
+ "token"
2281
+ ];
2282
+ const deletedAuthCookies = [];
2283
+ for (const [cookieName] of lastCookieMap) {
2284
+ const isAuthCookie = authCookieNames.some(
2285
+ (authName) => cookieName.toLowerCase().includes(authName.toLowerCase())
2286
+ );
2287
+ if (isAuthCookie && !currentCookieMap.has(cookieName)) {
2288
+ deletedAuthCookies.push(cookieName);
2289
+ }
2290
+ }
2291
+ if (deletedAuthCookies.length > 0) {
2292
+ logger.info("Auth cookies deleted detected", {
2293
+ deletedCookies: deletedAuthCookies,
2294
+ previousCookieCount: lastCookieMap.size,
2295
+ currentCookieCount: currentCookieMap.size
2296
+ });
2297
+ return true;
2298
+ }
2299
+ const hadTenneoAuthAccess = lastCookieMap.has(COOKIE_NAMES.accessToken) || lastCookieMap.has("tenneo_auth_access_token");
2300
+ const hasTenneoAuthAccess = currentCookieMap.has(COOKIE_NAMES.accessToken) || currentCookieMap.has("tenneo_auth_access_token");
2301
+ if (hadTenneoAuthAccess && !hasTenneoAuthAccess) {
2302
+ logger.info("tenneo auth access token cookie deleted detected");
2303
+ return true;
2304
+ }
2305
+ const hadTenneoAuthRefresh = lastCookieMap.has(COOKIE_NAMES.refreshToken) || lastCookieMap.has("tenneo_auth_refresh_token");
2306
+ const hasTenneoAuthRefresh = currentCookieMap.has(COOKIE_NAMES.refreshToken) || currentCookieMap.has("tenneo_auth_refresh_token");
2307
+ if (hadTenneoAuthRefresh && !hasTenneoAuthRefresh) {
2308
+ logger.info("tenneo auth refresh token cookie deleted detected");
2309
+ return true;
2310
+ }
2311
+ if (lastCookieMap.size > currentCookieMap.size && lastCookieMap.size - currentCookieMap.size >= 1) {
2312
+ const deletedCookies = [];
2313
+ for (const [cookieName] of lastCookieMap) {
2314
+ if (!currentCookieMap.has(cookieName)) deletedCookies.push(cookieName);
2315
+ }
2316
+ const deletedAuthCookiesCount = deletedCookies.filter(
2317
+ (name) => authCookieNames.some(
2318
+ (authName) => name.toLowerCase().includes(authName.toLowerCase())
2319
+ )
2320
+ ).length;
2321
+ if (deletedAuthCookiesCount > 0) {
2322
+ logger.info("Auth cookies deleted detected (count-based)", {
2323
+ deletedCookies,
2324
+ deletedAuthCount: deletedAuthCookiesCount,
2325
+ previousCount: lastCookieMap.size,
2326
+ currentCount: currentCookieMap.size
2327
+ });
2328
+ return true;
2329
+ }
2330
+ }
2331
+ }
2332
+ lastCookieSnapshot = currentSnapshot;
2333
+ lastCookieMap = new Map(currentCookieMap);
2334
+ return false;
2335
+ } catch (error) {
2336
+ logger.warn("Error checking cookies", {
2337
+ error: error instanceof Error ? error.message : String(error)
2338
+ });
2339
+ return false;
2340
+ }
2341
+ }
2342
+ function handleCookieDeletion() {
2343
+ try {
2344
+ logger.info("Cookies deleted detected - clearing session and redirecting to login");
2345
+ const forceLoginSet = getStorage(getStorageKey("force_login")) === "true";
2346
+ const logoutInProgress = getStorage(getStorageKey("logout_in_progress")) === "true";
2347
+ if (forceLoginSet || logoutInProgress) {
2348
+ logger.debug(
2349
+ "Force login or logout in progress - skipping bootstrap from cookie watcher (avoids duplicate by-code API call)"
2350
+ );
2351
+ return;
2352
+ }
2353
+ clearAuthStorage();
2354
+ try {
2355
+ if (typeof window !== "undefined") {
2356
+ const forceLogin = window.localStorage.getItem(getStorageKey("force_login"));
2357
+ const logoutInProgressPreserve = window.localStorage.getItem(
2358
+ getStorageKey("logout_in_progress")
2359
+ );
2360
+ clearAuthSessionStorage();
2361
+ if (forceLogin === "true")
2362
+ window.localStorage.setItem(getStorageKey("force_login"), "true");
2363
+ if (logoutInProgressPreserve === "true")
2364
+ window.localStorage.setItem(getStorageKey("logout_in_progress"), "true");
2365
+ } else {
2366
+ clearAuthSessionStorage();
2367
+ }
2368
+ } catch (error) {
2369
+ logger.warn("Failed to clear sessionStorage", {
2370
+ error: error instanceof Error ? error.message : String(error)
2371
+ });
2372
+ }
2373
+ const cb = getSessionClearedCallback();
2374
+ if (cb) {
2375
+ Promise.resolve(cb()).catch((error) => {
2376
+ logger.error("Failed to redirect to login after cookie deletion", {
2377
+ error: error instanceof Error ? error.message : String(error)
2378
+ });
2379
+ });
2380
+ }
2381
+ } catch (error) {
2382
+ logger.error("Error handling cookie deletion", {
2383
+ error: error instanceof Error ? error.message : String(error)
2384
+ });
2385
+ }
2386
+ }
2387
+ function startCookieWatcher() {
2388
+ if (isWatching) {
2389
+ logger.debug("Cookie watcher already running");
2390
+ return;
2391
+ }
2392
+ isWatching = true;
2393
+ lastCookieSnapshot = getCookieSnapshot();
2394
+ lastCookieMap = getCookieMap();
2395
+ const initialCookies = getCookieNames();
2396
+ const authCookies = initialCookies.filter(
2397
+ (name) => name.toLowerCase().includes("tenneo_auth") || name.toLowerCase().includes("auth") || name.toLowerCase().includes("token")
2398
+ );
2399
+ logger.debug("Cookie watcher started", {
2400
+ initialCookieCount: initialCookies.length,
2401
+ authCookieCount: authCookies.length,
2402
+ authCookies,
2403
+ allCookies: initialCookies
2404
+ });
2405
+ watchInterval = setInterval(async () => {
2406
+ const logoutInProgress = typeof window !== "undefined" && window.localStorage.getItem(getStorageKey("logout_in_progress")) === "true";
2407
+ if (logoutInProgress) {
2408
+ logger.debug("Logout detected. Cookie watcher will not trigger bootstrap.");
2409
+ lastCookieSnapshot = getCookieSnapshot();
2410
+ lastCookieMap = getCookieMap();
2411
+ return;
2412
+ }
2413
+ const cookiesDeleted = checkCookiesDeleted();
2414
+ if (cookiesDeleted) {
2415
+ logger.info("Cookies deleted detected by cookie watcher");
2416
+ if (watchInterval) {
2417
+ clearInterval(watchInterval);
2418
+ watchInterval = null;
2419
+ }
2420
+ isWatching = false;
2421
+ try {
2422
+ await handleCookieDeletion();
2423
+ } catch (error) {
2424
+ logger.error("Error handling cookie deletion, will restart watcher", {
2425
+ error: error instanceof Error ? error.message : String(error)
2426
+ });
2427
+ } finally {
2428
+ queueMicrotask(() => {
2429
+ if (!isWatching) startCookieWatcher();
2430
+ });
2431
+ }
2432
+ }
2433
+ }, 1e3);
2434
+ logger.info("Cookie watcher started - monitoring for cookie deletion");
2435
+ }
2436
+ function stopCookieWatcher() {
2437
+ isWatching = false;
2438
+ if (watchInterval) {
2439
+ clearInterval(watchInterval);
2440
+ watchInterval = null;
2441
+ }
2442
+ lastCookieSnapshot = "";
2443
+ logger.debug("Cookie watcher stopped");
2444
+ }
2445
+ function resetCookieWatcher() {
2446
+ lastCookieSnapshot = getCookieSnapshot();
2447
+ lastCookieMap = getCookieMap();
2448
+ const currentCookies = getCookieNames();
2449
+ const authCookies = currentCookies.filter(
2450
+ (name) => name.toLowerCase().includes("tenneo_auth") || name.toLowerCase().includes("auth") || name.toLowerCase().includes("token")
2451
+ );
2452
+ logger.debug("Cookie watcher reset", {
2453
+ currentCookieCount: currentCookies.length,
2454
+ authCookieCount: authCookies.length,
2455
+ authCookies
2456
+ });
2457
+ }
2458
+ function stopStorageWatcher() {
2459
+ const win = window;
2460
+ const intervalId = win.__tenneo_auth_storage_watcher_interval;
2461
+ if (intervalId) {
2462
+ clearInterval(intervalId);
2463
+ delete win.__tenneo_auth_storage_watcher_interval;
2464
+ }
2465
+ }
2466
+
2467
+ // src/utils/cookies.ts
2468
+ function clearClientCookies() {
2469
+ try {
2470
+ const cookies = document.cookie.split(";");
2471
+ for (const cookie of cookies) {
2472
+ const eqPos = cookie.indexOf("=");
2473
+ const name = eqPos > -1 ? cookie.slice(0, eqPos).trim() : cookie.trim();
2474
+ if (!name) continue;
2475
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
2476
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=${window.location.hostname}`;
2477
+ }
2478
+ logger.debug("Client-side cookies cleared (best-effort)");
2479
+ } catch (error) {
2480
+ logger.warn("Failed to clear client cookies", {
2481
+ error: error instanceof Error ? error.message : String(error)
2482
+ });
2483
+ }
2484
+ }
2485
+
2486
+ // src/application/logout.ts
2487
+ function buildUrl(baseUrl, path) {
2488
+ const url = new URL(baseUrl);
2489
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
2490
+ url.pathname = url.pathname.endsWith("/") ? `${url.pathname}${normalizedPath.slice(1)}` : `${url.pathname}${normalizedPath}`;
2491
+ return url.toString();
2492
+ }
2493
+ async function callServerLogout(logoutUrl) {
2494
+ try {
2495
+ const response = await fetch(logoutUrl, {
2496
+ method: "POST",
2497
+ headers: {
2498
+ "Content-Type": "application/x-www-form-urlencoded",
2499
+ "X-Requested-With": "XMLHttpRequest",
2500
+ Accept: "application/json"
2501
+ },
2502
+ credentials: "include"
2503
+ });
2504
+ const data = await response.json().catch(() => ({}));
2505
+ logger.info("Server logout response (POST)", { response: data });
2506
+ if (data && (data.success === true || data.logout === true)) {
2507
+ return { success: true, response: data };
2508
+ }
2509
+ logger.warn("Server logout response invalid (POST)", { response: data });
2510
+ return { success: false, response: data };
2511
+ } catch (error) {
2512
+ logger.debug("POST logout failed, trying GET", {
2513
+ error: error instanceof Error ? error.message : String(error)
2514
+ });
2515
+ try {
2516
+ const response = await fetch(logoutUrl, {
2517
+ method: "GET",
2518
+ headers: {
2519
+ "X-Requested-With": "XMLHttpRequest",
2520
+ Accept: "application/json"
2521
+ },
2522
+ credentials: "include"
2523
+ });
2524
+ const data = await response.json().catch(() => ({}));
2525
+ logger.info("Server logout response (GET)", { response: data });
2526
+ if (data && (data.success === true || data.logout === true)) {
2527
+ return { success: true, response: data };
2528
+ }
2529
+ logger.warn("Server logout response invalid (GET)", { response: data });
2530
+ return { success: false, response: data };
2531
+ } catch (getError) {
2532
+ logger.warn("Server logout failed (both POST and GET)", {
2533
+ postError: error instanceof Error ? error.message : String(error),
2534
+ getError: getError instanceof Error ? getError.message : String(getError)
2535
+ });
2536
+ return { success: false };
2537
+ }
2538
+ }
2539
+ }
2540
+ function performLocalLogoutCleanup() {
2541
+ logger.debug("Performing local logout cleanup");
2542
+ clearClientCookies();
2543
+ clearAllStorage();
2544
+ logger.info("Local logout cleanup completed");
2545
+ }
2546
+ async function logout(tenantKey) {
2547
+ setStorage(getStorageKey("logout_in_progress"), "true");
2548
+ setStorage(getStorageKey("force_login"), "true");
2549
+ if (tenantKey?.trim()) setStorage(getStorageKey("preserved_tenant_key"), tenantKey.trim());
2550
+ if (getStorage(getStorageKey("logout_processing")) === "true") {
2551
+ logger.debug("Logout already in progress \u2013 skipping duplicate call");
2552
+ return { success: true, message: "Logout already in progress." };
2553
+ }
2554
+ setStorage(getStorageKey("logout_processing"), "true");
2555
+ try {
2556
+ stopStorageWatcher();
2557
+ stopCookieWatcher();
2558
+ const apiAuthBaseUrl = Config.getApiAuthBaseUrl();
2559
+ let serverLogoutSuccess = false;
2560
+ let serverResponse;
2561
+ if (apiAuthBaseUrl) {
2562
+ const logoutUrl = buildUrl(apiAuthBaseUrl, "account/logout");
2563
+ logger.info("Calling auth server logout", { logoutUrl });
2564
+ const logoutResult = await callServerLogout(logoutUrl);
2565
+ serverLogoutSuccess = logoutResult.success;
2566
+ serverResponse = logoutResult.response;
2567
+ }
2568
+ performLocalLogoutCleanup();
2569
+ const preservedTenantKey = getStorage(getStorageKey("preserved_tenant_key"));
2570
+ if (preservedTenantKey) {
2571
+ setStorage(getStorageKey("props_config"), JSON.stringify({ tenantKey: preservedTenantKey }));
2572
+ removeStorage(getStorageKey("preserved_tenant_key"));
2573
+ }
2574
+ resetCookieWatcher();
2575
+ removeStorage(getStorageKey("logout_processing"));
2576
+ logger.info("Logout completed \u2013 flags set, storage cleared, fresh login required", {
2577
+ serverLogoutSuccess,
2578
+ serverResponse
2579
+ });
2580
+ emitAuthEvent(AuthEventNames.LOGOUT_COMPLETED, {
2581
+ message: serverLogoutSuccess ? "Server logout success" : "Local logout only"
2582
+ });
2583
+ return {
2584
+ success: true,
2585
+ message: serverLogoutSuccess ? serverResponse?.message || "Logout successful. Session cookies cleared. Fresh login required." : "Logged out locally. Fresh login required (server logout may have failed).",
2586
+ serverResponse
2587
+ };
2588
+ } catch (error) {
2589
+ logger.error("Logout failed unexpectedly", {
2590
+ error: error instanceof Error ? error.message : String(error)
2591
+ });
2592
+ performLocalLogoutCleanup();
2593
+ const preservedTenantKey = getStorage(getStorageKey("preserved_tenant_key"));
2594
+ if (preservedTenantKey) {
2595
+ setStorage(getStorageKey("props_config"), JSON.stringify({ tenantKey: preservedTenantKey }));
2596
+ removeStorage(getStorageKey("preserved_tenant_key"));
2597
+ }
2598
+ removeStorage(getStorageKey("logout_processing"));
2599
+ return {
2600
+ success: true,
2601
+ message: "Logged out locally due to an error. Fresh login required."
2602
+ };
2603
+ }
2604
+ }
2605
+ var AuthContext = createContext(void 0);
2606
+ function useAuthContext() {
2607
+ const context = useContext(AuthContext);
2608
+ if (!context) {
2609
+ throw new Error("useAuthContext must be used within AuthProvider");
2610
+ }
2611
+ return context;
2612
+ }
2613
+
2614
+ // src/infrastructure/tenant/tenant.api.ts
2615
+ var ongoingRequests = /* @__PURE__ */ new Map();
2616
+ var DEFAULT_SCOPES2 = OAUTH_CONSTANTS.SCOPES;
2617
+ function getResolvedTenantBaseUrl() {
2618
+ let base = Config.getApiTenantResolutionBaseUrl();
2619
+ if (typeof window !== "undefined" && (!base || base.startsWith("/"))) {
2620
+ base = window.location.origin + (base || "/api");
2621
+ }
2622
+ return base;
2623
+ }
2624
+ var PUBLIC_CODE_PREFIX = "public-";
2625
+ function getTenantByCodeUrl(tenantCode) {
2626
+ const apiTenantResolutionBaseUrl = getResolvedTenantBaseUrl();
2627
+ const base = apiTenantResolutionBaseUrl.replace(/\/api\/v1\/?$/, "") || apiTenantResolutionBaseUrl;
2628
+ return `${base}/api/tenant/v1/public/tenants/by-code/${encodeURIComponent(tenantCode)}`;
2629
+ }
2630
+ async function resolveTenant(tenantKey) {
2631
+ if (!tenantKey || tenantKey.trim() === "") {
2632
+ logger.debug("tenantKey is empty or invalid, skipping API call");
2633
+ return null;
2634
+ }
2635
+ const tenantCode = tenantKey.trim();
2636
+ const requestKey = `resolveTenant:${tenantCode}`;
2637
+ if (ongoingRequests.has(requestKey)) {
2638
+ logger.debug("Tenant resolution reused (in-flight)", { tenantCode });
2639
+ return ongoingRequests.get(requestKey);
2640
+ }
2641
+ logger.info("Tenant resolution API call", { tenantCode });
2642
+ const requestPromise = (async () => {
2643
+ try {
2644
+ const url = getTenantByCodeUrl(tenantCode);
2645
+ logger.debug("GET tenant by-code", { url });
2646
+ let data;
2647
+ try {
2648
+ const response = await fetch(url, { method: "GET" });
2649
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
2650
+ data = await response.json();
2651
+ logger.debug("API response received", { tenantCode });
2652
+ } catch (error) {
2653
+ logger.warn("API call failed", { tenantCode, error });
2654
+ return null;
2655
+ }
2656
+ if (!data || typeof data !== "object") return null;
2657
+ const res = data;
2658
+ if (!res.id || !res.clientCode) return null;
2659
+ const authServer = Config.getAuthServerUrl();
2660
+ const redirectUri = Config.getRedirectUri();
2661
+ if (!authServer || !redirectUri) return null;
2662
+ const clientCodeWithPrefix = res.clientCode.startsWith(PUBLIC_CODE_PREFIX) ? res.clientCode : `${PUBLIC_CODE_PREFIX}${res.clientCode}`;
2663
+ const config = {
2664
+ tenantId: res.id,
2665
+ authServer,
2666
+ tenantKey: tenantCode,
2667
+ client: {
2668
+ clientId: res.clientCode,
2669
+ clientCode: clientCodeWithPrefix,
2670
+ redirectUri,
2671
+ scopes: DEFAULT_SCOPES2
2672
+ }
2673
+ };
2674
+ logger.debug("Resolved tenant config", { tenantCode });
2675
+ return config;
2676
+ } catch (error) {
2677
+ logger.warn("Unexpected error resolving tenant", { tenantCode, error });
2678
+ return null;
2679
+ } finally {
2680
+ ongoingRequests.delete(requestKey);
2681
+ }
2682
+ })();
2683
+ ongoingRequests.set(requestKey, requestPromise);
2684
+ return requestPromise;
2685
+ }
2686
+
2687
+ // src/browser/adapters.ts
2688
+ function createConsoleLogger() {
2689
+ return {
2690
+ info(message, meta) {
2691
+ if (typeof console !== "undefined") console.info(message, meta ?? "");
2692
+ },
2693
+ warn(message, meta) {
2694
+ if (typeof console !== "undefined") console.warn(message, meta ?? "");
2695
+ },
2696
+ error(message, meta) {
2697
+ if (typeof console !== "undefined") console.error(message, meta ?? "");
2698
+ },
2699
+ debug(message, meta) {
2700
+ if (typeof console !== "undefined") console.debug(message, meta ?? "");
2701
+ }
2702
+ };
2703
+ }
2704
+ function createBrowserUrlProvider() {
2705
+ return {
2706
+ getCurrentUrl() {
2707
+ if (typeof window === "undefined") {
2708
+ return { href: "", origin: "", pathname: "/", search: "", hostname: "" };
2709
+ }
2710
+ const w = window;
2711
+ const loc = w.location;
2712
+ return {
2713
+ href: loc.href,
2714
+ origin: loc.origin,
2715
+ pathname: loc.pathname,
2716
+ search: loc.search,
2717
+ hostname: loc.hostname
2718
+ };
2719
+ },
2720
+ redirect(url) {
2721
+ if (typeof window !== "undefined") window.location.replace(url);
2722
+ },
2723
+ replaceState(url) {
2724
+ if (typeof window === "undefined") return;
2725
+ try {
2726
+ const title = typeof document !== "undefined" ? document.title : "";
2727
+ window.history.replaceState({}, title, url);
2728
+ } catch {
2729
+ }
2730
+ }
2731
+ };
2732
+ }
2733
+ function createConfigProvider() {
2734
+ return {
2735
+ getApiBaseUrl: () => Config.getApiAuthBaseUrl(),
2736
+ getAuthServerUrl: () => Config.getAuthServerUrl(),
2737
+ getRedirectUri: () => Config.getRedirectUri(),
2738
+ getClientId: () => Config.getClientId(),
2739
+ getTenantId: () => Config.getTenantId(),
2740
+ getClientCode: () => Config.getClientCode(),
2741
+ getBasePath: () => Config.getBasePath()
2742
+ };
2743
+ }
2744
+ function createTenantResolver() {
2745
+ return { resolve: (tenantKey) => resolveTenant(tenantKey) };
2746
+ }
2747
+ function createBrowserCookieClearer() {
2748
+ return {
2749
+ clearCookies() {
2750
+ if (typeof document === "undefined" || typeof window === "undefined") return;
2751
+ try {
2752
+ const cookies = document.cookie.split(";");
2753
+ cookies.forEach((cookie) => {
2754
+ const eqPos = cookie.indexOf("=");
2755
+ const name = eqPos > -1 ? cookie.substring(0, eqPos).trim() : cookie.trim();
2756
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
2757
+ const domain = window.location.hostname;
2758
+ const parts = domain.split(".");
2759
+ if (parts.length > 1) {
2760
+ const parentDomain = "." + parts.slice(-2).join(".");
2761
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;domain=${parentDomain}`;
2762
+ }
2763
+ });
2764
+ } catch {
2765
+ }
2766
+ }
2767
+ };
2768
+ }
2769
+
2770
+ // src/strategies/oauth2-pkce/OAuth2PkceStrategy.ts
2771
+ function createOAuth2PkceStrategy() {
2772
+ return {
2773
+ async initiate(tenantConfig) {
2774
+ storeTenantConfig(tenantConfig);
2775
+ await initiateAuthFlow(tenantConfig);
2776
+ },
2777
+ async handleCallback() {
2778
+ return handleCallback();
2779
+ },
2780
+ async refresh() {
2781
+ return refreshAccessToken();
2782
+ },
2783
+ async logout(options) {
2784
+ await logout(options?.tenantKey);
2785
+ }
2786
+ };
2787
+ }
2788
+
2789
+ // src/application/engine/eventBus.ts
2790
+ function createEventBus() {
2791
+ const listeners2 = /* @__PURE__ */ new Set();
2792
+ return {
2793
+ subscribe(listener) {
2794
+ listeners2.add(listener);
2795
+ return () => listeners2.delete(listener);
2796
+ },
2797
+ emit(state) {
2798
+ listeners2.forEach((fn) => {
2799
+ try {
2800
+ fn(state);
2801
+ } catch {
2802
+ }
2803
+ });
2804
+ }
2805
+ };
2806
+ }
2807
+
2808
+ // src/application/engine/createEngine.ts
2809
+ function stateEquals(a, b) {
2810
+ return a.status === b.status && a.accessToken === b.accessToken && a.expiresAt === b.expiresAt && a.error === b.error;
2811
+ }
2812
+ function deriveState(config) {
2813
+ setAuthRuntime(config);
2814
+ const accessToken = getAccessToken();
2815
+ const expiresAt = getExpiresAt();
2816
+ const expired = isAccessTokenExpired();
2817
+ if (accessToken && !expired) {
2818
+ return {
2819
+ status: "authenticated",
2820
+ accessToken,
2821
+ expiresAt,
2822
+ error: null
2823
+ };
2824
+ }
2825
+ if (accessToken && expired) {
2826
+ return {
2827
+ status: "error",
2828
+ accessToken,
2829
+ expiresAt,
2830
+ error: "Token expired"
2831
+ };
2832
+ }
2833
+ return {
2834
+ status: "unauthenticated",
2835
+ accessToken: null,
2836
+ expiresAt: null,
2837
+ error: null
2838
+ };
2839
+ }
2840
+ function createAuthEngine(config) {
2841
+ const frozenConfig = Object.freeze({ ...config });
2842
+ const bus = createEventBus();
2843
+ let lastEmittedState = null;
2844
+ const strategy = frozenConfig.strategy ?? createOAuth2PkceStrategy();
2845
+ const emitState = () => {
2846
+ const next = deriveState(frozenConfig);
2847
+ if (lastEmittedState !== null && stateEquals(lastEmittedState, next)) return;
2848
+ lastEmittedState = next;
2849
+ bus.emit(next);
2850
+ };
2851
+ return {
2852
+ async init() {
2853
+ setAuthRuntime(frozenConfig);
2854
+ frozenConfig.logger.debug("Engine init: running bootstrap");
2855
+ await runBootstrap();
2856
+ emitState();
2857
+ },
2858
+ async login() {
2859
+ setAuthRuntime(frozenConfig);
2860
+ frozenConfig.logger.debug("Engine login: running bootstrap (may redirect)");
2861
+ await runBootstrap();
2862
+ emitState();
2863
+ },
2864
+ async logout(options) {
2865
+ setAuthRuntime(frozenConfig);
2866
+ frozenConfig.logger.debug("Engine logout");
2867
+ await strategy.logout(options);
2868
+ emitState();
2869
+ },
2870
+ async refresh() {
2871
+ setAuthRuntime(frozenConfig);
2872
+ frozenConfig.logger.debug("Engine refresh");
2873
+ try {
2874
+ const token = await strategy.refresh();
2875
+ emitState();
2876
+ return token;
2877
+ } catch (e) {
2878
+ emitState();
2879
+ throw e;
2880
+ }
2881
+ },
2882
+ async getAccessToken() {
2883
+ setAuthRuntime(frozenConfig);
2884
+ return ensureValidAccessToken();
2885
+ },
2886
+ async handleCallback() {
2887
+ setAuthRuntime(frozenConfig);
2888
+ frozenConfig.logger.debug("Engine handleCallback");
2889
+ const handled = await strategy.handleCallback();
2890
+ emitState();
2891
+ return handled;
2892
+ },
2893
+ subscribe(listener) {
2894
+ return bus.subscribe(listener);
2895
+ },
2896
+ getState() {
2897
+ return deriveState(frozenConfig);
2898
+ }
2899
+ };
2900
+ }
2901
+ function AuthProvider({
2902
+ children,
2903
+ autoInit = true,
2904
+ tenantCode,
2905
+ appId = "",
2906
+ storageAdapter
2907
+ }) {
2908
+ const resolvedTenantKey = tenantCode ?? Config.getClientCode();
2909
+ const namespace = useMemo(
2910
+ () => ({ tenantId: resolvedTenantKey ?? "", appId: appId ?? "" }),
2911
+ [resolvedTenantKey, appId]
2912
+ );
2913
+ const initRanRef = useRef(false);
2914
+ const engineRef = useRef(null);
2915
+ useLayoutEffect(() => {
2916
+ if (tenantCode) {
2917
+ setConfig({ CLIENTCODE: tenantCode });
2918
+ }
2919
+ return () => {
2920
+ clearConfigOverrides();
2921
+ };
2922
+ }, [tenantCode]);
2923
+ useLayoutEffect(() => {
2924
+ setStorageKeyPrefix(resolvedTenantKey ?? "");
2925
+ const adapter = storageAdapter !== void 0 && storageAdapter !== null ? storageAdapter : createSecureSessionStorageAdapter(namespace);
2926
+ setStorageContext(adapter, namespace);
2927
+ const runtime = {
2928
+ storageAdapter: adapter,
2929
+ storageNamespace: namespace,
2930
+ logger: createConsoleLogger(),
2931
+ urlProvider: createBrowserUrlProvider(),
2932
+ configProvider: createConfigProvider(),
2933
+ tenantResolver: createTenantResolver(),
2934
+ clearCookies: createBrowserCookieClearer()
2935
+ };
2936
+ setAuthRuntime(runtime);
2937
+ engineRef.current = createAuthEngine(runtime);
2938
+ setDefaultAuthEngine(engineRef.current);
2939
+ return () => {
2940
+ setDefaultAuthEngine(null);
2941
+ engineRef.current = null;
2942
+ setAuthRuntime(null);
2943
+ setStorageContext(null, { tenantId: "", appId: "" });
2944
+ setStorageKeyPrefix("");
2945
+ };
2946
+ }, [resolvedTenantKey, appId, storageAdapter]);
2947
+ useEffect(() => {
2948
+ const resolvedTenantKey2 = tenantCode ?? Config.getClientCode();
2949
+ logger.debug("[AuthProvider] Props from host app", {
2950
+ tenantCode: tenantCode ?? "(not provided)",
2951
+ appId: appId ?? "",
2952
+ resolvedTenantKey: resolvedTenantKey2
2953
+ });
2954
+ if (resolvedTenantKey2) {
2955
+ const previousTenantKey = getStorage(getStorageKey("tenant_key"));
2956
+ const tenantKeyChanged = previousTenantKey && previousTenantKey !== resolvedTenantKey2;
2957
+ if (tenantKeyChanged) {
2958
+ logger.debug("[AuthProvider] Tenant switch detected", { previous: previousTenantKey, next: resolvedTenantKey2 });
2959
+ if (typeof window !== "undefined") {
2960
+ const currentUrl = window.location.href;
2961
+ const isCallback = currentUrl.includes("code=") || currentUrl.includes("state=");
2962
+ if (isCallback) {
2963
+ const cleanUrl2 = window.location.origin + window.location.pathname;
2964
+ window.history.replaceState({}, document.title, cleanUrl2);
2965
+ }
2966
+ }
2967
+ removeStorage(getStorageKey("access_token"));
2968
+ removeStorage(getStorageKey("refresh_token"));
2969
+ removeStorage(getStorageKey("expires_at"));
2970
+ removeStorage(getStorageKey("id_token"));
2971
+ removeStorage(getStorageKey("state"));
2972
+ removeStorage(getStorageKey("code_verifier"));
2973
+ removeStorage(getStorageKey("callback_processed"));
2974
+ clearCallbackProcessing();
2975
+ clearFlowFlags();
2976
+ removeStorage(getStorageKey("tenant_id"));
2977
+ if (typeof window !== "undefined") {
2978
+ window.localStorage.removeItem(getStorageKey("force_login"));
2979
+ window.localStorage.removeItem(getStorageKey("logout_in_progress"));
2980
+ }
2981
+ setStorage(getStorageKey("props_changed"), "true");
2982
+ setStorage(getStorageKey("tenant_switched"), "true");
2983
+ setStorage(getStorageKey("new_tenant_key"), resolvedTenantKey2);
2984
+ setState({
2985
+ isAuthenticated: false,
2986
+ accessToken: null,
2987
+ isLoading: true,
2988
+ error: null
2989
+ });
2990
+ } else if (previousTenantKey) {
2991
+ logger.debug("[AuthProvider] TenantKey unchanged", { tenantKey: resolvedTenantKey2 });
2992
+ }
2993
+ setStorage(getStorageKey("props_config"), JSON.stringify({ tenantKey: resolvedTenantKey2 }));
2994
+ setStorage(getStorageKey("tenant_key"), resolvedTenantKey2);
2995
+ } else {
2996
+ logger.debug("[AuthProvider] No tenant code determined");
2997
+ }
2998
+ }, [tenantCode, appId]);
2999
+ const [state, setState] = useState({
3000
+ isAuthenticated: false,
3001
+ accessToken: null,
3002
+ isLoading: autoInit,
3003
+ error: null
3004
+ });
3005
+ const syncAuthState = useCallback(() => {
3006
+ try {
3007
+ const engine = engineRef.current;
3008
+ let authenticated;
3009
+ let accessToken;
3010
+ let error = null;
3011
+ if (engine) {
3012
+ const s = engine.getState();
3013
+ authenticated = s.status === "authenticated" && !!s.accessToken;
3014
+ accessToken = s.accessToken;
3015
+ error = s.error;
3016
+ } else {
3017
+ accessToken = getAccessToken();
3018
+ authenticated = !!accessToken && !isAccessTokenExpired();
3019
+ }
3020
+ setState({
3021
+ isAuthenticated: authenticated,
3022
+ accessToken,
3023
+ isLoading: false,
3024
+ error
3025
+ });
3026
+ if (authenticated) {
3027
+ } else {
3028
+ stopStorageWatcher();
3029
+ stopCookieWatcher();
3030
+ }
3031
+ } catch (error) {
3032
+ stopStorageWatcher();
3033
+ stopCookieWatcher();
3034
+ setState({
3035
+ isAuthenticated: false,
3036
+ accessToken: null,
3037
+ isLoading: false,
3038
+ error: error instanceof Error ? error.message : "Unknown error"
3039
+ });
3040
+ }
3041
+ }, []);
3042
+ useEffect(() => {
3043
+ if (!autoInit) return;
3044
+ const tenantSwitched = getStorage(getStorageKey("tenant_switched")) === "true";
3045
+ const propsChanged = getStorage(getStorageKey("props_changed")) === "true";
3046
+ if (!tenantSwitched && !propsChanged) return;
3047
+ let cancelled = false;
3048
+ const reinit = async () => {
3049
+ try {
3050
+ await engineRef.current?.init();
3051
+ if (!cancelled) {
3052
+ syncAuthState();
3053
+ }
3054
+ } finally {
3055
+ removeStorage(getStorageKey("props_changed"));
3056
+ }
3057
+ };
3058
+ reinit();
3059
+ return () => {
3060
+ cancelled = true;
3061
+ };
3062
+ }, [autoInit, tenantCode, syncAuthState]);
3063
+ useEffect(() => {
3064
+ if (!autoInit) {
3065
+ setState((prev) => ({ ...prev, isLoading: false }));
3066
+ return;
3067
+ }
3068
+ if (initRanRef.current) return;
3069
+ initRanRef.current = true;
3070
+ let cancelled = false;
3071
+ const init = async () => {
3072
+ try {
3073
+ await engineRef.current?.init();
3074
+ if (!cancelled) syncAuthState();
3075
+ } catch (error) {
3076
+ stopStorageWatcher();
3077
+ stopCookieWatcher();
3078
+ if (!cancelled) {
3079
+ setState({
3080
+ isAuthenticated: false,
3081
+ accessToken: null,
3082
+ isLoading: false,
3083
+ error: error instanceof Error ? error.message : "Authentication initialization failed"
3084
+ });
3085
+ }
3086
+ }
3087
+ };
3088
+ init();
3089
+ return () => {
3090
+ cancelled = true;
3091
+ initRanRef.current = false;
3092
+ stopStorageWatcher();
3093
+ stopCookieWatcher();
3094
+ };
3095
+ }, [autoInit, syncAuthState]);
3096
+ const login = useCallback(async () => {
3097
+ setState((prev) => ({ ...prev, isLoading: true }));
3098
+ await engineRef.current?.login();
3099
+ syncAuthState();
3100
+ }, [syncAuthState]);
3101
+ const logout2 = useCallback(async () => {
3102
+ stopStorageWatcher();
3103
+ stopCookieWatcher();
3104
+ setState((prev) => ({ ...prev, isLoading: true }));
3105
+ try {
3106
+ await engineRef.current?.logout({ tenantKey: tenantCode });
3107
+ resetCookieWatcher();
3108
+ } finally {
3109
+ stopStorageWatcher();
3110
+ stopCookieWatcher();
3111
+ setState({
3112
+ isAuthenticated: false,
3113
+ accessToken: null,
3114
+ isLoading: false,
3115
+ error: null
3116
+ });
3117
+ }
3118
+ }, [tenantCode]);
3119
+ const refreshToken = useCallback(async () => {
3120
+ try {
3121
+ const newToken = await engineRef.current?.refresh() ?? "";
3122
+ syncAuthState();
3123
+ return newToken;
3124
+ } catch (error) {
3125
+ await logout2();
3126
+ throw error;
3127
+ }
3128
+ }, [syncAuthState, logout2]);
3129
+ const getValidAccessToken = useCallback(async () => {
3130
+ try {
3131
+ const token = await engineRef.current?.getAccessToken();
3132
+ if (!token) throw new Error("Not authenticated");
3133
+ syncAuthState();
3134
+ return token;
3135
+ } catch (error) {
3136
+ await logout2();
3137
+ throw error;
3138
+ }
3139
+ }, [syncAuthState, logout2]);
3140
+ const setTokens = useCallback((accessToken) => {
3141
+ setState({
3142
+ isAuthenticated: true,
3143
+ accessToken,
3144
+ isLoading: false,
3145
+ error: null
3146
+ });
3147
+ }, []);
3148
+ const contextValue = useMemo(
3149
+ () => ({
3150
+ ...state,
3151
+ login,
3152
+ logout: logout2,
3153
+ refreshToken,
3154
+ getAccessToken: getValidAccessToken,
3155
+ setTokens
3156
+ }),
3157
+ [state, login, logout2, refreshToken, getValidAccessToken, setTokens]
3158
+ );
3159
+ return /* @__PURE__ */ jsx(AuthContext.Provider, { value: contextValue, children });
3160
+ }
3161
+ var PLUGIN_DISPLAY_NAME = "Tenneo Auth";
3162
+ function AuthCallback({
3163
+ loginPath = "/login",
3164
+ successPath = "/dashboard",
3165
+ homePath = "/"
3166
+ }) {
3167
+ const { setTokens } = useAuthContext();
3168
+ const handledRef = useRef(false);
3169
+ const [status, setStatus] = useState("idle");
3170
+ const [message, setMessage] = useState("");
3171
+ useEffect(() => {
3172
+ if (handledRef.current) return;
3173
+ handledRef.current = true;
3174
+ const urlParams = new URLSearchParams(window.location.search);
3175
+ const hasCallbackParams = urlParams.has("code") || urlParams.has("error");
3176
+ if (!hasCallbackParams) {
3177
+ window.location.replace(loginPath || homePath);
3178
+ return;
3179
+ }
3180
+ const error = urlParams.get("error");
3181
+ const errorDescription = urlParams.get("error_description");
3182
+ if (error) {
3183
+ setStatus("error");
3184
+ setMessage(`${error}: ${errorDescription || "Authorization failed"}`);
3185
+ window.history.replaceState({}, document.title, window.location.origin + window.location.pathname);
3186
+ return;
3187
+ }
3188
+ const runCallback = async () => {
3189
+ setStatus("loading");
3190
+ setMessage("Completing sign-in...");
3191
+ const handled = await handleCallback();
3192
+ if (handled) {
3193
+ const token = getAccessToken();
3194
+ if (token) setTokens(token);
3195
+ setStatus("success");
3196
+ setMessage("Authentication successful. Redirecting...");
3197
+ window.location.replace(successPath);
3198
+ } else {
3199
+ setStatus("error");
3200
+ setMessage("Authentication failed. Please try again.");
3201
+ window.history.replaceState({}, document.title, window.location.origin + window.location.pathname);
3202
+ }
3203
+ };
3204
+ runCallback();
3205
+ }, [homePath, loginPath, successPath, setTokens]);
3206
+ const containerStyle = {
3207
+ minHeight: "200px",
3208
+ display: "flex",
3209
+ flexDirection: "column",
3210
+ alignItems: "center",
3211
+ justifyContent: "center",
3212
+ padding: 32,
3213
+ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
3214
+ textAlign: "center"
3215
+ };
3216
+ const titleStyle = {
3217
+ margin: 0,
3218
+ fontSize: "1.25rem",
3219
+ fontWeight: 600,
3220
+ color: "#1a1a1a",
3221
+ marginBottom: 8
3222
+ };
3223
+ const messageStyle = {
3224
+ margin: 0,
3225
+ fontSize: "0.9375rem",
3226
+ color: "#555",
3227
+ lineHeight: 1.5,
3228
+ maxWidth: 360
3229
+ };
3230
+ const errorBoxStyle = {
3231
+ marginTop: 16,
3232
+ padding: "12px 16px",
3233
+ backgroundColor: "#fef2f2",
3234
+ border: "1px solid #fecaca",
3235
+ borderRadius: 8,
3236
+ color: "#991b1b",
3237
+ fontSize: "0.875rem",
3238
+ maxWidth: 360
3239
+ };
3240
+ const retryLinkStyle = {
3241
+ marginTop: 16,
3242
+ fontSize: "0.875rem",
3243
+ color: "#2563eb",
3244
+ textDecoration: "none",
3245
+ cursor: "pointer"
3246
+ };
3247
+ const spinnerStyle = {
3248
+ width: 28,
3249
+ height: 28,
3250
+ border: "3px solid #e5e7eb",
3251
+ borderTopColor: "#2563eb",
3252
+ borderRadius: "50%",
3253
+ animation: "auth-callback-spin 0.8s linear infinite",
3254
+ marginBottom: 16
3255
+ };
3256
+ const pluginNameStyle = {
3257
+ position: "absolute",
3258
+ top: 24,
3259
+ left: "50%",
3260
+ transform: "translateX(-50%)",
3261
+ margin: 0,
3262
+ fontSize: "0.875rem",
3263
+ fontWeight: 600,
3264
+ color: "#6b7280",
3265
+ letterSpacing: "0.02em"
3266
+ };
3267
+ return /* @__PURE__ */ jsxs("div", { style: { ...containerStyle, position: "relative" }, children: [
3268
+ /* @__PURE__ */ jsx("style", { children: `@keyframes auth-callback-spin { to { transform: rotate(360deg); } }` }),
3269
+ /* @__PURE__ */ jsx("p", { style: pluginNameStyle, children: PLUGIN_DISPLAY_NAME }),
3270
+ status === "loading" && /* @__PURE__ */ jsxs(Fragment, { children: [
3271
+ /* @__PURE__ */ jsx("div", { style: spinnerStyle, "aria-hidden": true }),
3272
+ /* @__PURE__ */ jsx("h2", { style: titleStyle, children: "Signing you in..." }),
3273
+ /* @__PURE__ */ jsx("p", { style: messageStyle, children: "Please wait while we complete authentication." })
3274
+ ] }),
3275
+ status === "success" && /* @__PURE__ */ jsxs(Fragment, { children: [
3276
+ /* @__PURE__ */ jsx("h2", { style: titleStyle, children: "Signed in successfully" }),
3277
+ /* @__PURE__ */ jsx("p", { style: messageStyle, children: "Redirecting you now." })
3278
+ ] }),
3279
+ status === "error" && /* @__PURE__ */ jsxs(Fragment, { children: [
3280
+ /* @__PURE__ */ jsx("h2", { style: { ...titleStyle, color: "#991b1b" }, children: "Authentication failed" }),
3281
+ /* @__PURE__ */ jsx("div", { style: errorBoxStyle, children: message }),
3282
+ /* @__PURE__ */ jsx(
3283
+ "a",
3284
+ {
3285
+ href: loginPath,
3286
+ style: retryLinkStyle,
3287
+ onClick: (e) => {
3288
+ e.preventDefault();
3289
+ window.location.href = loginPath;
3290
+ },
3291
+ children: "Try again"
3292
+ }
3293
+ )
3294
+ ] }),
3295
+ status === "idle" && /* @__PURE__ */ jsxs(Fragment, { children: [
3296
+ /* @__PURE__ */ jsx("div", { style: spinnerStyle, "aria-hidden": true }),
3297
+ /* @__PURE__ */ jsx("h2", { style: titleStyle, children: "Preparing callback..." }),
3298
+ /* @__PURE__ */ jsx("p", { style: messageStyle, children: "Setting up authentication." })
3299
+ ] })
3300
+ ] });
3301
+ }
3302
+
3303
+ // src/hooks/useAuth.ts
3304
+ function useAuth() {
3305
+ return useAuthContext();
3306
+ }
3307
+ function ProtectedRoute({
3308
+ children,
3309
+ fallback = null,
3310
+ autoLogin = true
3311
+ }) {
3312
+ const { isAuthenticated, getAccessToken: getAccessToken3, login } = useAuth();
3313
+ const [ready, setReady] = useState(false);
3314
+ const loginTriggeredRef = useRef(false);
3315
+ useEffect(() => {
3316
+ let cancelled = false;
3317
+ const run = async () => {
3318
+ if (isAuthenticated) {
3319
+ if (!cancelled) setReady(true);
3320
+ return;
3321
+ }
3322
+ try {
3323
+ await getAccessToken3();
3324
+ if (!cancelled) setReady(true);
3325
+ } catch {
3326
+ if (autoLogin && !loginTriggeredRef.current) {
3327
+ loginTriggeredRef.current = true;
3328
+ await login();
3329
+ }
3330
+ if (!cancelled) setReady(false);
3331
+ }
3332
+ };
3333
+ run();
3334
+ return () => {
3335
+ cancelled = true;
3336
+ };
3337
+ }, [autoLogin, getAccessToken3, isAuthenticated, login]);
3338
+ if (!ready || !isAuthenticated) {
3339
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
3340
+ }
3341
+ return /* @__PURE__ */ jsx(Fragment, { children });
3342
+ }
3343
+ function useAuthInit() {
3344
+ const [isLoading, setIsLoading] = useState(false);
3345
+ const [error, setError] = useState(null);
3346
+ const inProgressRef = useRef(false);
3347
+ const mountedRef = useRef(true);
3348
+ useEffect(() => {
3349
+ return () => {
3350
+ mountedRef.current = false;
3351
+ };
3352
+ }, []);
3353
+ const init = useCallback(async () => {
3354
+ if (inProgressRef.current) {
3355
+ return;
3356
+ }
3357
+ inProgressRef.current = true;
3358
+ if (mountedRef.current) {
3359
+ setIsLoading(true);
3360
+ setError(null);
3361
+ }
3362
+ try {
3363
+ await bootstrap();
3364
+ } catch (err) {
3365
+ if (mountedRef.current) {
3366
+ setError(
3367
+ err instanceof Error ? err.message : "Authentication initialization failed"
3368
+ );
3369
+ }
3370
+ } finally {
3371
+ inProgressRef.current = false;
3372
+ if (mountedRef.current) {
3373
+ setIsLoading(false);
3374
+ }
3375
+ }
3376
+ }, []);
3377
+ return useMemo(
3378
+ () => ({ isLoading, error, init }),
3379
+ [isLoading, error, init]
3380
+ );
3381
+ }
3382
+
3383
+ export { AuthCallback, AuthEventNames, AuthProvider, Config, OAUTH_CONSTANTS, ProtectedRoute, STORAGE_KEYS2 as STORAGE_KEYS, STORAGE_KEY_PREFIX, SanitizedError, bootstrap, buildStorageKey, createAuthEngine, createCookieStorageAdapter, createMemoryStorageAdapter, createOAuth2PkceStrategy, createSecureSessionStorageAdapter, emitAuthEvent, enforceHttps, ensureValidAccessToken, extractTenantKey, getAccessToken2 as getAccessToken, getAuthRuntime, getConfig, getEnvConfig, getStorageAdapter, getStorageNamespace, getAccessToken as getStoredAccessToken, hasEnvFallback, isAccessTokenExpired, isCallbackUrl, logout, refreshAccessToken, resetCookieWatcher, resolveConfig, resolveTenant, sanitizeErrorMessage, setAuthRuntime, setConfig, setStorageContext, startCookieWatcher, stopCookieWatcher, subscribeAuthEvent, useAuth, useAuthContext, useAuthInit };
3384
+ //# sourceMappingURL=index.mjs.map
3385
+ //# sourceMappingURL=index.mjs.map