@spidy092/auth-client 3.1.2 → 3.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.cjs +117 -61
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +117 -61
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +290 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +290 -96
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +117 -61
- package/dist/preferences.cjs.map +1 -1
- package/dist/preferences.js +117 -61
- package/dist/preferences.js.map +1 -1
- package/dist/react/AuthProvider.cjs +266 -96
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +266 -96
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +117 -61
- package/dist/react/PreferencesProvider.cjs.map +1 -1
- package/dist/react/PreferencesProvider.js +117 -61
- package/dist/react/PreferencesProvider.js.map +1 -1
- package/dist/react/useAuth.cjs +7 -0
- package/dist/react/useAuth.cjs.map +1 -1
- package/dist/react/useAuth.js +7 -0
- package/dist/react/useAuth.js.map +1 -1
- package/dist/react/useSessionMonitor.cjs +157 -66
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +157 -66
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/package.json +1 -1
|
@@ -254,6 +254,44 @@ var config = {
|
|
|
254
254
|
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
255
255
|
persistRefreshToken: false
|
|
256
256
|
};
|
|
257
|
+
var RUNTIME_POLICY_DEFAULTS = {
|
|
258
|
+
tokenRefreshBuffer: 60,
|
|
259
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
260
|
+
enableSessionValidation: true,
|
|
261
|
+
enableProactiveRefresh: true,
|
|
262
|
+
validateOnVisibility: true
|
|
263
|
+
};
|
|
264
|
+
function validateRuntimePolicy(policy = {}) {
|
|
265
|
+
const numberInRange = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
|
|
266
|
+
const result = { ...RUNTIME_POLICY_DEFAULTS };
|
|
267
|
+
if (numberInRange(policy.tokenRefreshBuffer, 10, 300)) result.tokenRefreshBuffer = policy.tokenRefreshBuffer;
|
|
268
|
+
if (numberInRange(policy.sessionValidationInterval, 6e4, 864e5)) result.sessionValidationInterval = policy.sessionValidationInterval;
|
|
269
|
+
for (const key of ["enableSessionValidation", "enableProactiveRefresh", "validateOnVisibility"]) {
|
|
270
|
+
if (typeof policy[key] === "boolean") result[key] = policy[key];
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
function applyRuntimePolicy(policy = {}) {
|
|
275
|
+
config = { ...config, ...validateRuntimePolicy(policy) };
|
|
276
|
+
return getConfig();
|
|
277
|
+
}
|
|
278
|
+
async function loadRuntimePolicy() {
|
|
279
|
+
const { authBaseUrl, clientKey } = config;
|
|
280
|
+
if (!authBaseUrl || !clientKey || typeof fetch !== "function") return getConfig();
|
|
281
|
+
try {
|
|
282
|
+
const response = await fetch(`${authBaseUrl.replace(/\/+$/, "")}/clients/${encodeURIComponent(clientKey)}/config`, {
|
|
283
|
+
method: "GET",
|
|
284
|
+
credentials: "include",
|
|
285
|
+
headers: { Accept: "application/json" }
|
|
286
|
+
});
|
|
287
|
+
if (!response.ok) throw new Error(`runtime policy HTTP ${response.status}`);
|
|
288
|
+
const payload = await response.json();
|
|
289
|
+
return applyRuntimePolicy(payload.authentication_policy || {});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.warn("[auth-client] Runtime policy unavailable; using safe defaults", error.message);
|
|
292
|
+
return applyRuntimePolicy({});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
257
295
|
function setConfig(customConfig = {}) {
|
|
258
296
|
if (!customConfig.clientKey || !customConfig.authBaseUrl) {
|
|
259
297
|
throw new Error("Missing required config: clientKey and authBaseUrl are required");
|
|
@@ -339,6 +377,28 @@ function clearLoginLock() {
|
|
|
339
377
|
} catch {
|
|
340
378
|
}
|
|
341
379
|
}
|
|
380
|
+
async function reportClientDiagnostic(safe) {
|
|
381
|
+
if (typeof fetch !== "function" || typeof getToken !== "function") return;
|
|
382
|
+
if (safe.outcome === "SUCCESS" && !safe.event.startsWith("PROFILE_") && !safe.event.startsWith("SESSION_")) return;
|
|
383
|
+
const { authBaseUrl } = getConfig();
|
|
384
|
+
const token = getToken();
|
|
385
|
+
if (!authBaseUrl || !token) return;
|
|
386
|
+
try {
|
|
387
|
+
await fetch(`${authBaseUrl.replace(/\/+$/, "")}/client-telemetry`, {
|
|
388
|
+
method: "POST",
|
|
389
|
+
credentials: "include",
|
|
390
|
+
keepalive: true,
|
|
391
|
+
headers: {
|
|
392
|
+
"Content-Type": "application/json",
|
|
393
|
+
Authorization: `Bearer ${token}`,
|
|
394
|
+
"X-Correlation-ID": safe.correlationId,
|
|
395
|
+
"X-Request-ID": safe.correlationId
|
|
396
|
+
},
|
|
397
|
+
body: JSON.stringify(safe)
|
|
398
|
+
});
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}
|
|
342
402
|
async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
343
403
|
const context = getDiagnosticContext();
|
|
344
404
|
const safe = {
|
|
@@ -355,12 +415,13 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
355
415
|
};
|
|
356
416
|
const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
|
|
357
417
|
console[method]("[auth-client]", safe);
|
|
418
|
+
void reportClientDiagnostic(safe);
|
|
358
419
|
return safe;
|
|
359
420
|
}
|
|
360
421
|
|
|
361
422
|
// core.js
|
|
362
423
|
var callbackProcessed = false;
|
|
363
|
-
function login(clientKeyArg, redirectUriArg) {
|
|
424
|
+
function login(clientKeyArg, redirectUriArg, options = {}) {
|
|
364
425
|
resetCallbackState();
|
|
365
426
|
const {
|
|
366
427
|
clientKey: defaultClientKey,
|
|
@@ -383,23 +444,24 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
383
444
|
sessionStorage.setItem("originalApp", clientKey);
|
|
384
445
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
385
446
|
if (isRouterMode()) {
|
|
386
|
-
return routerLogin(clientKey, redirectUri);
|
|
447
|
+
return routerLogin(clientKey, redirectUri, options);
|
|
387
448
|
} else {
|
|
388
|
-
return clientLogin(clientKey, redirectUri);
|
|
449
|
+
return clientLogin(clientKey, redirectUri, options);
|
|
389
450
|
}
|
|
390
451
|
}
|
|
391
|
-
function routerLogin(clientKey, redirectUri) {
|
|
452
|
+
function routerLogin(clientKey, redirectUri, options = {}) {
|
|
392
453
|
const { authBaseUrl } = getConfig();
|
|
393
454
|
const params = new URLSearchParams();
|
|
394
455
|
if (redirectUri) {
|
|
395
456
|
params.append("redirect_uri", redirectUri);
|
|
396
457
|
}
|
|
458
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
397
459
|
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
398
460
|
const query = params.toString();
|
|
399
461
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
400
462
|
window.location.href = backendLoginUrl;
|
|
401
463
|
}
|
|
402
|
-
function clientLogin(clientKey, redirectUri) {
|
|
464
|
+
function clientLogin(clientKey, redirectUri, options = {}) {
|
|
403
465
|
const { accountUiUrl } = getConfig();
|
|
404
466
|
const params = new URLSearchParams({
|
|
405
467
|
client: clientKey
|
|
@@ -407,6 +469,7 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
407
469
|
if (redirectUri) {
|
|
408
470
|
params.append("redirect_uri", redirectUri);
|
|
409
471
|
}
|
|
472
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
410
473
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
411
474
|
window.location.href = centralizedLoginUrl;
|
|
412
475
|
}
|
|
@@ -532,6 +595,28 @@ function resetCallbackState() {
|
|
|
532
595
|
}
|
|
533
596
|
var refreshInProgress = false;
|
|
534
597
|
var refreshPromise = null;
|
|
598
|
+
async function withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest) {
|
|
599
|
+
var _a;
|
|
600
|
+
const lockName = `auth-refresh-${clientKey}`;
|
|
601
|
+
const run = async () => {
|
|
602
|
+
try {
|
|
603
|
+
const persistedToken = localStorage.getItem("authToken");
|
|
604
|
+
if (tokenBeforeRefresh && persistedToken && persistedToken !== tokenBeforeRefresh) {
|
|
605
|
+
setToken(persistedToken);
|
|
606
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REUSED_CROSS_TAB", "SUCCESS", "CROSS_TAB_ROTATION", {
|
|
607
|
+
clientKey
|
|
608
|
+
});
|
|
609
|
+
return persistedToken;
|
|
610
|
+
}
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
return refreshRequest();
|
|
614
|
+
};
|
|
615
|
+
if (typeof navigator !== "undefined" && ((_a = navigator.locks) == null ? void 0 : _a.request)) {
|
|
616
|
+
return navigator.locks.request(lockName, run);
|
|
617
|
+
}
|
|
618
|
+
return run();
|
|
619
|
+
}
|
|
535
620
|
async function refreshToken() {
|
|
536
621
|
const { clientKey, authBaseUrl } = getConfig();
|
|
537
622
|
if (refreshInProgress && refreshPromise) {
|
|
@@ -540,70 +625,74 @@ async function refreshToken() {
|
|
|
540
625
|
}
|
|
541
626
|
refreshInProgress = true;
|
|
542
627
|
refreshPromise = (async () => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
hasStoredRefreshToken: !!storedRefreshToken
|
|
550
|
-
});
|
|
551
|
-
const requestOptions = {
|
|
552
|
-
method: "POST",
|
|
553
|
-
credentials: "include",
|
|
554
|
-
// ✅ Include httpOnly cookies (for HTTPS)
|
|
555
|
-
headers: {
|
|
556
|
-
...diagnosticHeaders(),
|
|
557
|
-
"Content-Type": "application/json"
|
|
558
|
-
}
|
|
559
|
-
};
|
|
560
|
-
if (storedRefreshToken) {
|
|
561
|
-
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
562
|
-
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
563
|
-
}
|
|
564
|
-
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
565
|
-
if (!response.ok) {
|
|
566
|
-
const errorText = await response.text();
|
|
567
|
-
let serverCode = null;
|
|
568
|
-
try {
|
|
569
|
-
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
570
|
-
} catch {
|
|
571
|
-
}
|
|
572
|
-
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
628
|
+
const tokenBeforeRefresh = getToken();
|
|
629
|
+
const refreshRequest = async () => {
|
|
630
|
+
var _a, _b, _c, _d, _e, _f;
|
|
631
|
+
try {
|
|
632
|
+
const storedRefreshToken = getRefreshToken();
|
|
633
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
573
634
|
clientKey,
|
|
574
|
-
|
|
635
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
636
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
575
637
|
});
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
638
|
+
const requestOptions = {
|
|
639
|
+
method: "POST",
|
|
640
|
+
credentials: "include",
|
|
641
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
642
|
+
headers: {
|
|
643
|
+
...diagnosticHeaders(),
|
|
644
|
+
"Content-Type": "application/json"
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
if (storedRefreshToken) {
|
|
648
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
649
|
+
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
650
|
+
}
|
|
651
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
652
|
+
if (!response.ok) {
|
|
653
|
+
const errorText = await response.text();
|
|
654
|
+
let serverCode = null;
|
|
655
|
+
try {
|
|
656
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
659
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
660
|
+
clientKey,
|
|
661
|
+
status: response.status
|
|
662
|
+
});
|
|
663
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
664
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
665
|
+
refreshError.status = response.status;
|
|
666
|
+
throw refreshError;
|
|
667
|
+
}
|
|
668
|
+
const data = await response.json();
|
|
669
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
670
|
+
if (!access_token) {
|
|
671
|
+
throw new Error("No access token in refresh response");
|
|
672
|
+
}
|
|
673
|
+
setToken(access_token);
|
|
674
|
+
if (new_refresh_token) {
|
|
675
|
+
setRefreshToken(new_refresh_token);
|
|
676
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
677
|
+
}
|
|
678
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
679
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
680
|
+
return access_token;
|
|
681
|
+
} catch (err) {
|
|
682
|
+
console.error("\u274C Token refresh error:", err);
|
|
683
|
+
const isAuthRejection = ((_c = err.message) == null ? void 0 : _c.includes("401")) || ((_d = err.message) == null ? void 0 : _d.includes("403")) || ((_e = err.message) == null ? void 0 : _e.includes("invalid_grant")) || ((_f = err.message) == null ? void 0 : _f.includes("Refresh failed: 4"));
|
|
684
|
+
if (isAuthRejection) {
|
|
685
|
+
clearToken();
|
|
686
|
+
clearRefreshToken();
|
|
687
|
+
}
|
|
688
|
+
throw err;
|
|
600
689
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
})
|
|
690
|
+
};
|
|
691
|
+
return withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest);
|
|
692
|
+
})().finally(() => {
|
|
693
|
+
refreshInProgress = false;
|
|
694
|
+
refreshPromise = null;
|
|
695
|
+
});
|
|
607
696
|
return refreshPromise;
|
|
608
697
|
}
|
|
609
698
|
async function validateCurrentSession() {
|
|
@@ -1168,6 +1257,8 @@ var PreferencesContext = (0, import_react3.createContext)(null);
|
|
|
1168
1257
|
var auth = {
|
|
1169
1258
|
// 🔧 Config
|
|
1170
1259
|
setConfig,
|
|
1260
|
+
applyRuntimePolicy,
|
|
1261
|
+
loadRuntimePolicy,
|
|
1171
1262
|
getConfig,
|
|
1172
1263
|
isRouterMode,
|
|
1173
1264
|
// 🔐 Core flows
|