@spidy092/auth-client 3.1.1 → 3.1.3
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 +110 -61
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +110 -61
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +136 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +136 -68
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +110 -61
- package/dist/preferences.cjs.map +1 -1
- package/dist/preferences.js +110 -61
- package/dist/preferences.js.map +1 -1
- package/dist/react/AuthProvider.cjs +117 -66
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +117 -66
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +110 -61
- package/dist/react/PreferencesProvider.cjs.map +1 -1
- package/dist/react/PreferencesProvider.js +110 -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 +136 -68
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +136 -68
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -162,7 +162,7 @@ function shouldUseLocalStorage() {
|
|
|
162
162
|
return false;
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
function
|
|
165
|
+
function setRefreshToken(token) {
|
|
166
166
|
if (!token) {
|
|
167
167
|
clearRefreshToken();
|
|
168
168
|
return;
|
|
@@ -268,6 +268,44 @@ var config = {
|
|
|
268
268
|
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
269
269
|
persistRefreshToken: false
|
|
270
270
|
};
|
|
271
|
+
var RUNTIME_POLICY_DEFAULTS = {
|
|
272
|
+
tokenRefreshBuffer: 60,
|
|
273
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
274
|
+
enableSessionValidation: true,
|
|
275
|
+
enableProactiveRefresh: true,
|
|
276
|
+
validateOnVisibility: true
|
|
277
|
+
};
|
|
278
|
+
function validateRuntimePolicy(policy = {}) {
|
|
279
|
+
const numberInRange = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
|
|
280
|
+
const result = { ...RUNTIME_POLICY_DEFAULTS };
|
|
281
|
+
if (numberInRange(policy.tokenRefreshBuffer, 10, 300)) result.tokenRefreshBuffer = policy.tokenRefreshBuffer;
|
|
282
|
+
if (numberInRange(policy.sessionValidationInterval, 6e4, 864e5)) result.sessionValidationInterval = policy.sessionValidationInterval;
|
|
283
|
+
for (const key of ["enableSessionValidation", "enableProactiveRefresh", "validateOnVisibility"]) {
|
|
284
|
+
if (typeof policy[key] === "boolean") result[key] = policy[key];
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
function applyRuntimePolicy(policy = {}) {
|
|
289
|
+
config = { ...config, ...validateRuntimePolicy(policy) };
|
|
290
|
+
return getConfig();
|
|
291
|
+
}
|
|
292
|
+
async function loadRuntimePolicy() {
|
|
293
|
+
const { authBaseUrl, clientKey } = config;
|
|
294
|
+
if (!authBaseUrl || !clientKey || typeof fetch !== "function") return getConfig();
|
|
295
|
+
try {
|
|
296
|
+
const response = await fetch(`${authBaseUrl.replace(/\/+$/, "")}/clients/${encodeURIComponent(clientKey)}/config`, {
|
|
297
|
+
method: "GET",
|
|
298
|
+
credentials: "include",
|
|
299
|
+
headers: { Accept: "application/json" }
|
|
300
|
+
});
|
|
301
|
+
if (!response.ok) throw new Error(`runtime policy HTTP ${response.status}`);
|
|
302
|
+
const payload = await response.json();
|
|
303
|
+
return applyRuntimePolicy(payload.authentication_policy || {});
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.warn("[auth-client] Runtime policy unavailable; using safe defaults", error.message);
|
|
306
|
+
return applyRuntimePolicy({});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
271
309
|
function setConfig(customConfig = {}) {
|
|
272
310
|
if (!customConfig.clientKey || !customConfig.authBaseUrl) {
|
|
273
311
|
throw new Error("Missing required config: clientKey and authBaseUrl are required");
|
|
@@ -374,7 +412,7 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
374
412
|
|
|
375
413
|
// core.js
|
|
376
414
|
var callbackProcessed = false;
|
|
377
|
-
function login(clientKeyArg, redirectUriArg) {
|
|
415
|
+
function login(clientKeyArg, redirectUriArg, options = {}) {
|
|
378
416
|
resetCallbackState();
|
|
379
417
|
const {
|
|
380
418
|
clientKey: defaultClientKey,
|
|
@@ -397,23 +435,24 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
397
435
|
sessionStorage.setItem("originalApp", clientKey);
|
|
398
436
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
399
437
|
if (isRouterMode()) {
|
|
400
|
-
return routerLogin(clientKey, redirectUri);
|
|
438
|
+
return routerLogin(clientKey, redirectUri, options);
|
|
401
439
|
} else {
|
|
402
|
-
return clientLogin(clientKey, redirectUri);
|
|
440
|
+
return clientLogin(clientKey, redirectUri, options);
|
|
403
441
|
}
|
|
404
442
|
}
|
|
405
|
-
function routerLogin(clientKey, redirectUri) {
|
|
443
|
+
function routerLogin(clientKey, redirectUri, options = {}) {
|
|
406
444
|
const { authBaseUrl } = getConfig();
|
|
407
445
|
const params = new URLSearchParams();
|
|
408
446
|
if (redirectUri) {
|
|
409
447
|
params.append("redirect_uri", redirectUri);
|
|
410
448
|
}
|
|
449
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
411
450
|
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
412
451
|
const query = params.toString();
|
|
413
452
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
414
453
|
window.location.href = backendLoginUrl;
|
|
415
454
|
}
|
|
416
|
-
function clientLogin(clientKey, redirectUri) {
|
|
455
|
+
function clientLogin(clientKey, redirectUri, options = {}) {
|
|
417
456
|
const { accountUiUrl } = getConfig();
|
|
418
457
|
const params = new URLSearchParams({
|
|
419
458
|
client: clientKey
|
|
@@ -421,6 +460,7 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
421
460
|
if (redirectUri) {
|
|
422
461
|
params.append("redirect_uri", redirectUri);
|
|
423
462
|
}
|
|
463
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
424
464
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
425
465
|
window.location.href = centralizedLoginUrl;
|
|
426
466
|
}
|
|
@@ -546,6 +586,28 @@ function resetCallbackState() {
|
|
|
546
586
|
}
|
|
547
587
|
var refreshInProgress = false;
|
|
548
588
|
var refreshPromise = null;
|
|
589
|
+
async function withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest) {
|
|
590
|
+
var _a;
|
|
591
|
+
const lockName = `auth-refresh-${clientKey}`;
|
|
592
|
+
const run = async () => {
|
|
593
|
+
try {
|
|
594
|
+
const persistedToken = localStorage.getItem("authToken");
|
|
595
|
+
if (tokenBeforeRefresh && persistedToken && persistedToken !== tokenBeforeRefresh) {
|
|
596
|
+
setToken(persistedToken);
|
|
597
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REUSED_CROSS_TAB", "SUCCESS", "CROSS_TAB_ROTATION", {
|
|
598
|
+
clientKey
|
|
599
|
+
});
|
|
600
|
+
return persistedToken;
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
return refreshRequest();
|
|
605
|
+
};
|
|
606
|
+
if (typeof navigator !== "undefined" && ((_a = navigator.locks) == null ? void 0 : _a.request)) {
|
|
607
|
+
return navigator.locks.request(lockName, run);
|
|
608
|
+
}
|
|
609
|
+
return run();
|
|
610
|
+
}
|
|
549
611
|
async function refreshToken() {
|
|
550
612
|
const { clientKey, authBaseUrl } = getConfig();
|
|
551
613
|
if (refreshInProgress && refreshPromise) {
|
|
@@ -554,70 +616,74 @@ async function refreshToken() {
|
|
|
554
616
|
}
|
|
555
617
|
refreshInProgress = true;
|
|
556
618
|
refreshPromise = (async () => {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
hasStoredRefreshToken: !!storedRefreshToken
|
|
564
|
-
});
|
|
565
|
-
const requestOptions = {
|
|
566
|
-
method: "POST",
|
|
567
|
-
credentials: "include",
|
|
568
|
-
// ✅ Include httpOnly cookies (for HTTPS)
|
|
569
|
-
headers: {
|
|
570
|
-
...diagnosticHeaders(),
|
|
571
|
-
"Content-Type": "application/json"
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
if (storedRefreshToken) {
|
|
575
|
-
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
576
|
-
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
577
|
-
}
|
|
578
|
-
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
579
|
-
if (!response.ok) {
|
|
580
|
-
const errorText = await response.text();
|
|
581
|
-
let serverCode = null;
|
|
582
|
-
try {
|
|
583
|
-
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
584
|
-
} catch {
|
|
585
|
-
}
|
|
586
|
-
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
619
|
+
const tokenBeforeRefresh = getToken();
|
|
620
|
+
const refreshRequest = async () => {
|
|
621
|
+
var _a, _b, _c, _d, _e, _f;
|
|
622
|
+
try {
|
|
623
|
+
const storedRefreshToken = getRefreshToken();
|
|
624
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
587
625
|
clientKey,
|
|
588
|
-
|
|
626
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
627
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
589
628
|
});
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
629
|
+
const requestOptions = {
|
|
630
|
+
method: "POST",
|
|
631
|
+
credentials: "include",
|
|
632
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
633
|
+
headers: {
|
|
634
|
+
...diagnosticHeaders(),
|
|
635
|
+
"Content-Type": "application/json"
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
if (storedRefreshToken) {
|
|
639
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
640
|
+
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
641
|
+
}
|
|
642
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
643
|
+
if (!response.ok) {
|
|
644
|
+
const errorText = await response.text();
|
|
645
|
+
let serverCode = null;
|
|
646
|
+
try {
|
|
647
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
648
|
+
} catch {
|
|
649
|
+
}
|
|
650
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
651
|
+
clientKey,
|
|
652
|
+
status: response.status
|
|
653
|
+
});
|
|
654
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
655
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
656
|
+
refreshError.status = response.status;
|
|
657
|
+
throw refreshError;
|
|
658
|
+
}
|
|
659
|
+
const data = await response.json();
|
|
660
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
661
|
+
if (!access_token) {
|
|
662
|
+
throw new Error("No access token in refresh response");
|
|
663
|
+
}
|
|
664
|
+
setToken(access_token);
|
|
665
|
+
if (new_refresh_token) {
|
|
666
|
+
setRefreshToken(new_refresh_token);
|
|
667
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
668
|
+
}
|
|
669
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
670
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
671
|
+
return access_token;
|
|
672
|
+
} catch (err) {
|
|
673
|
+
console.error("\u274C Token refresh error:", err);
|
|
674
|
+
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"));
|
|
675
|
+
if (isAuthRejection) {
|
|
676
|
+
clearToken();
|
|
677
|
+
clearRefreshToken();
|
|
678
|
+
}
|
|
679
|
+
throw err;
|
|
614
680
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
})
|
|
681
|
+
};
|
|
682
|
+
return withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest);
|
|
683
|
+
})().finally(() => {
|
|
684
|
+
refreshInProgress = false;
|
|
685
|
+
refreshPromise = null;
|
|
686
|
+
});
|
|
621
687
|
return refreshPromise;
|
|
622
688
|
}
|
|
623
689
|
async function validateCurrentSession() {
|
|
@@ -1438,6 +1504,8 @@ function usePreferences() {
|
|
|
1438
1504
|
var auth = {
|
|
1439
1505
|
// 🔧 Config
|
|
1440
1506
|
setConfig,
|
|
1507
|
+
applyRuntimePolicy,
|
|
1508
|
+
loadRuntimePolicy,
|
|
1441
1509
|
getConfig,
|
|
1442
1510
|
isRouterMode,
|
|
1443
1511
|
// 🔐 Core flows
|
|
@@ -1453,7 +1521,7 @@ var auth = {
|
|
|
1453
1521
|
getToken,
|
|
1454
1522
|
setToken,
|
|
1455
1523
|
clearToken,
|
|
1456
|
-
setRefreshToken
|
|
1524
|
+
setRefreshToken,
|
|
1457
1525
|
// ✅ Refresh token for HTTP dev
|
|
1458
1526
|
getRefreshToken,
|
|
1459
1527
|
clearRefreshToken,
|