@spidy092/auth-client 3.1.2 → 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 +94 -61
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +94 -61
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +134 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -66
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +94 -61
- package/dist/preferences.cjs.map +1 -1
- package/dist/preferences.js +94 -61
- package/dist/preferences.js.map +1 -1
- package/dist/react/AuthProvider.cjs +101 -66
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +101 -66
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +94 -61
- package/dist/react/PreferencesProvider.cjs.map +1 -1
- package/dist/react/PreferencesProvider.js +94 -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 +134 -66
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +134 -66
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -217,6 +217,44 @@ var config = {
|
|
|
217
217
|
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
218
218
|
persistRefreshToken: false
|
|
219
219
|
};
|
|
220
|
+
var RUNTIME_POLICY_DEFAULTS = {
|
|
221
|
+
tokenRefreshBuffer: 60,
|
|
222
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
223
|
+
enableSessionValidation: true,
|
|
224
|
+
enableProactiveRefresh: true,
|
|
225
|
+
validateOnVisibility: true
|
|
226
|
+
};
|
|
227
|
+
function validateRuntimePolicy(policy = {}) {
|
|
228
|
+
const numberInRange = (value, min, max) => Number.isInteger(value) && value >= min && value <= max;
|
|
229
|
+
const result = { ...RUNTIME_POLICY_DEFAULTS };
|
|
230
|
+
if (numberInRange(policy.tokenRefreshBuffer, 10, 300)) result.tokenRefreshBuffer = policy.tokenRefreshBuffer;
|
|
231
|
+
if (numberInRange(policy.sessionValidationInterval, 6e4, 864e5)) result.sessionValidationInterval = policy.sessionValidationInterval;
|
|
232
|
+
for (const key of ["enableSessionValidation", "enableProactiveRefresh", "validateOnVisibility"]) {
|
|
233
|
+
if (typeof policy[key] === "boolean") result[key] = policy[key];
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
function applyRuntimePolicy(policy = {}) {
|
|
238
|
+
config = { ...config, ...validateRuntimePolicy(policy) };
|
|
239
|
+
return getConfig();
|
|
240
|
+
}
|
|
241
|
+
async function loadRuntimePolicy() {
|
|
242
|
+
const { authBaseUrl, clientKey } = config;
|
|
243
|
+
if (!authBaseUrl || !clientKey || typeof fetch !== "function") return getConfig();
|
|
244
|
+
try {
|
|
245
|
+
const response = await fetch(`${authBaseUrl.replace(/\/+$/, "")}/clients/${encodeURIComponent(clientKey)}/config`, {
|
|
246
|
+
method: "GET",
|
|
247
|
+
credentials: "include",
|
|
248
|
+
headers: { Accept: "application/json" }
|
|
249
|
+
});
|
|
250
|
+
if (!response.ok) throw new Error(`runtime policy HTTP ${response.status}`);
|
|
251
|
+
const payload = await response.json();
|
|
252
|
+
return applyRuntimePolicy(payload.authentication_policy || {});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.warn("[auth-client] Runtime policy unavailable; using safe defaults", error.message);
|
|
255
|
+
return applyRuntimePolicy({});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
220
258
|
function setConfig(customConfig = {}) {
|
|
221
259
|
if (!customConfig.clientKey || !customConfig.authBaseUrl) {
|
|
222
260
|
throw new Error("Missing required config: clientKey and authBaseUrl are required");
|
|
@@ -323,7 +361,7 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
323
361
|
|
|
324
362
|
// core.js
|
|
325
363
|
var callbackProcessed = false;
|
|
326
|
-
function login(clientKeyArg, redirectUriArg) {
|
|
364
|
+
function login(clientKeyArg, redirectUriArg, options = {}) {
|
|
327
365
|
resetCallbackState();
|
|
328
366
|
const {
|
|
329
367
|
clientKey: defaultClientKey,
|
|
@@ -346,23 +384,24 @@ function login(clientKeyArg, redirectUriArg) {
|
|
|
346
384
|
sessionStorage.setItem("originalApp", clientKey);
|
|
347
385
|
sessionStorage.setItem("returnUrl", redirectUri);
|
|
348
386
|
if (isRouterMode()) {
|
|
349
|
-
return routerLogin(clientKey, redirectUri);
|
|
387
|
+
return routerLogin(clientKey, redirectUri, options);
|
|
350
388
|
} else {
|
|
351
|
-
return clientLogin(clientKey, redirectUri);
|
|
389
|
+
return clientLogin(clientKey, redirectUri, options);
|
|
352
390
|
}
|
|
353
391
|
}
|
|
354
|
-
function routerLogin(clientKey, redirectUri) {
|
|
392
|
+
function routerLogin(clientKey, redirectUri, options = {}) {
|
|
355
393
|
const { authBaseUrl } = getConfig();
|
|
356
394
|
const params = new URLSearchParams();
|
|
357
395
|
if (redirectUri) {
|
|
358
396
|
params.append("redirect_uri", redirectUri);
|
|
359
397
|
}
|
|
398
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
360
399
|
params.append("correlation_id", getDiagnosticContext().correlationId);
|
|
361
400
|
const query = params.toString();
|
|
362
401
|
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
363
402
|
window.location.href = backendLoginUrl;
|
|
364
403
|
}
|
|
365
|
-
function clientLogin(clientKey, redirectUri) {
|
|
404
|
+
function clientLogin(clientKey, redirectUri, options = {}) {
|
|
366
405
|
const { accountUiUrl } = getConfig();
|
|
367
406
|
const params = new URLSearchParams({
|
|
368
407
|
client: clientKey
|
|
@@ -370,6 +409,7 @@ function clientLogin(clientKey, redirectUri) {
|
|
|
370
409
|
if (redirectUri) {
|
|
371
410
|
params.append("redirect_uri", redirectUri);
|
|
372
411
|
}
|
|
412
|
+
if (options.switchAccount || options.switch_account) params.append("switch_account", "true");
|
|
373
413
|
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
374
414
|
window.location.href = centralizedLoginUrl;
|
|
375
415
|
}
|
|
@@ -495,6 +535,28 @@ function resetCallbackState() {
|
|
|
495
535
|
}
|
|
496
536
|
var refreshInProgress = false;
|
|
497
537
|
var refreshPromise = null;
|
|
538
|
+
async function withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest) {
|
|
539
|
+
var _a;
|
|
540
|
+
const lockName = `auth-refresh-${clientKey}`;
|
|
541
|
+
const run = async () => {
|
|
542
|
+
try {
|
|
543
|
+
const persistedToken = localStorage.getItem("authToken");
|
|
544
|
+
if (tokenBeforeRefresh && persistedToken && persistedToken !== tokenBeforeRefresh) {
|
|
545
|
+
setToken(persistedToken);
|
|
546
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REUSED_CROSS_TAB", "SUCCESS", "CROSS_TAB_ROTATION", {
|
|
547
|
+
clientKey
|
|
548
|
+
});
|
|
549
|
+
return persistedToken;
|
|
550
|
+
}
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
return refreshRequest();
|
|
554
|
+
};
|
|
555
|
+
if (typeof navigator !== "undefined" && ((_a = navigator.locks) == null ? void 0 : _a.request)) {
|
|
556
|
+
return navigator.locks.request(lockName, run);
|
|
557
|
+
}
|
|
558
|
+
return run();
|
|
559
|
+
}
|
|
498
560
|
async function refreshToken() {
|
|
499
561
|
const { clientKey, authBaseUrl } = getConfig();
|
|
500
562
|
if (refreshInProgress && refreshPromise) {
|
|
@@ -503,70 +565,74 @@ async function refreshToken() {
|
|
|
503
565
|
}
|
|
504
566
|
refreshInProgress = true;
|
|
505
567
|
refreshPromise = (async () => {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
hasStoredRefreshToken: !!storedRefreshToken
|
|
513
|
-
});
|
|
514
|
-
const requestOptions = {
|
|
515
|
-
method: "POST",
|
|
516
|
-
credentials: "include",
|
|
517
|
-
// ✅ Include httpOnly cookies (for HTTPS)
|
|
518
|
-
headers: {
|
|
519
|
-
...diagnosticHeaders(),
|
|
520
|
-
"Content-Type": "application/json"
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
if (storedRefreshToken) {
|
|
524
|
-
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
525
|
-
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
526
|
-
}
|
|
527
|
-
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
528
|
-
if (!response.ok) {
|
|
529
|
-
const errorText = await response.text();
|
|
530
|
-
let serverCode = null;
|
|
531
|
-
try {
|
|
532
|
-
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
533
|
-
} catch {
|
|
534
|
-
}
|
|
535
|
-
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
568
|
+
const tokenBeforeRefresh = getToken();
|
|
569
|
+
const refreshRequest = async () => {
|
|
570
|
+
var _a, _b, _c, _d, _e, _f;
|
|
571
|
+
try {
|
|
572
|
+
const storedRefreshToken = getRefreshToken();
|
|
573
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
536
574
|
clientKey,
|
|
537
|
-
|
|
575
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
576
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
538
577
|
});
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
578
|
+
const requestOptions = {
|
|
579
|
+
method: "POST",
|
|
580
|
+
credentials: "include",
|
|
581
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
582
|
+
headers: {
|
|
583
|
+
...diagnosticHeaders(),
|
|
584
|
+
"Content-Type": "application/json"
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
if (storedRefreshToken) {
|
|
588
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
589
|
+
console.log("\u{1F4E6} Sending refresh token in body only (Header skipped) v3.0.2");
|
|
590
|
+
}
|
|
591
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
592
|
+
if (!response.ok) {
|
|
593
|
+
const errorText = await response.text();
|
|
594
|
+
let serverCode = null;
|
|
595
|
+
try {
|
|
596
|
+
serverCode = ((_a = JSON.parse(errorText)) == null ? void 0 : _a.error) || ((_b = JSON.parse(errorText)) == null ? void 0 : _b.code);
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
emitAuthDiagnostic("TOKEN_REFRESH_REJECTED", "FAILURE", serverCode || `HTTP_${response.status}`, {
|
|
600
|
+
clientKey,
|
|
601
|
+
status: response.status
|
|
602
|
+
});
|
|
603
|
+
const refreshError = new Error(`Refresh failed: ${response.status}`);
|
|
604
|
+
refreshError.code = serverCode || `HTTP_${response.status}`;
|
|
605
|
+
refreshError.status = response.status;
|
|
606
|
+
throw refreshError;
|
|
607
|
+
}
|
|
608
|
+
const data = await response.json();
|
|
609
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
610
|
+
if (!access_token) {
|
|
611
|
+
throw new Error("No access token in refresh response");
|
|
612
|
+
}
|
|
613
|
+
setToken(access_token);
|
|
614
|
+
if (new_refresh_token) {
|
|
615
|
+
setRefreshToken(new_refresh_token);
|
|
616
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
617
|
+
}
|
|
618
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
619
|
+
emitAuthDiagnostic("TOKEN_REFRESH_COMPLETED", "SUCCESS", "NONE", { clientKey });
|
|
620
|
+
return access_token;
|
|
621
|
+
} catch (err) {
|
|
622
|
+
console.error("\u274C Token refresh error:", err);
|
|
623
|
+
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"));
|
|
624
|
+
if (isAuthRejection) {
|
|
625
|
+
clearToken();
|
|
626
|
+
clearRefreshToken();
|
|
627
|
+
}
|
|
628
|
+
throw err;
|
|
563
629
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
})
|
|
630
|
+
};
|
|
631
|
+
return withCrossTabRefreshLock(clientKey, tokenBeforeRefresh, refreshRequest);
|
|
632
|
+
})().finally(() => {
|
|
633
|
+
refreshInProgress = false;
|
|
634
|
+
refreshPromise = null;
|
|
635
|
+
});
|
|
570
636
|
return refreshPromise;
|
|
571
637
|
}
|
|
572
638
|
async function validateCurrentSession() {
|
|
@@ -1387,6 +1453,8 @@ function usePreferences() {
|
|
|
1387
1453
|
var auth = {
|
|
1388
1454
|
// 🔧 Config
|
|
1389
1455
|
setConfig,
|
|
1456
|
+
applyRuntimePolicy,
|
|
1457
|
+
loadRuntimePolicy,
|
|
1390
1458
|
getConfig,
|
|
1391
1459
|
isRouterMode,
|
|
1392
1460
|
// 🔐 Core flows
|