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