@oxyhq/core 2.3.2 → 2.4.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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/AuthManager.js +8 -5
- package/dist/cjs/mixins/OxyServices.auth.js +24 -1
- package/dist/cjs/mixins/OxyServices.fedcm.js +25 -6
- package/dist/cjs/mixins/OxyServices.popup.js +16 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/AuthManager.js +8 -5
- package/dist/esm/mixins/OxyServices.auth.js +24 -1
- package/dist/esm/mixins/OxyServices.fedcm.js +25 -6
- package/dist/esm/mixins/OxyServices.popup.js +16 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/AuthManager.d.ts +3 -3
- package/dist/types/AuthManagerTypes.d.ts +17 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/mixins/OxyServices.auth.d.ts +20 -1
- package/dist/types/mixins/OxyServices.fedcm.d.ts +1 -1
- package/package.json +1 -1
- package/src/AuthManager.ts +9 -5
- package/src/AuthManagerTypes.ts +18 -0
- package/src/index.ts +1 -0
- package/src/mixins/OxyServices.auth.ts +44 -1
- package/src/mixins/OxyServices.fedcm.ts +26 -6
- package/src/mixins/OxyServices.popup.ts +17 -0
- package/src/mixins/__tests__/fedcm.test.ts +63 -2
- package/src/mixins/__tests__/popup.test.ts +67 -0
package/dist/cjs/AuthManager.js
CHANGED
|
@@ -733,9 +733,10 @@ class AuthManager {
|
|
|
733
733
|
* Returns the active user on success, or `null` when neither path
|
|
734
734
|
* restored a session.
|
|
735
735
|
*/
|
|
736
|
-
async initialize() {
|
|
737
|
-
// 1. Cookie path (preferred).
|
|
738
|
-
|
|
736
|
+
async initialize(options = {}) {
|
|
737
|
+
// 1. Cookie path (preferred). Forward the optional cold-boot fail-fast
|
|
738
|
+
// timeout so a cross-domain stall cannot hang provider init.
|
|
739
|
+
const cookieResult = await this.restoreFromCookies(options);
|
|
739
740
|
if (cookieResult.accounts.length > 0) {
|
|
740
741
|
return this.currentUser;
|
|
741
742
|
}
|
|
@@ -952,7 +953,7 @@ class AuthManager {
|
|
|
952
953
|
* proceed unauthenticated. State is NOT cleared on failure; existing
|
|
953
954
|
* accounts (if any) remain intact.
|
|
954
955
|
*/
|
|
955
|
-
async restoreFromCookies() {
|
|
956
|
+
async restoreFromCookies(options = {}) {
|
|
956
957
|
// Cross-tab cascade debounce. If we restored within the last
|
|
957
958
|
// _RESTORE_DEBOUNCE_MS for the currently-active slot, skip the network
|
|
958
959
|
// round-trip and return the cached registry verbatim. A burst of N
|
|
@@ -970,7 +971,9 @@ class AuthManager {
|
|
|
970
971
|
}
|
|
971
972
|
let snapshot;
|
|
972
973
|
try {
|
|
973
|
-
|
|
974
|
+
// Forward the optional cold-boot fail-fast timeout. Undefined (the warm
|
|
975
|
+
// cross-tab cascade default) preserves the wait-indefinitely behaviour.
|
|
976
|
+
snapshot = await this.oxyServices.refreshAllSessions({ timeout: options.timeout });
|
|
974
977
|
}
|
|
975
978
|
catch {
|
|
976
979
|
return { accounts: [], activeAuthuser: null };
|
|
@@ -454,19 +454,42 @@ function OxyServicesAuthMixin(Base) {
|
|
|
454
454
|
* tokens do. Each access token still needs to be planted via
|
|
455
455
|
* `setTokens(...)` (or per-account in-memory storage) at the consumer.
|
|
456
456
|
*/
|
|
457
|
-
async refreshAllSessions() {
|
|
457
|
+
async refreshAllSessions(options = {}) {
|
|
458
458
|
const url = `${this.getSessionBaseUrl().replace(/\/$/, '')}/auth/refresh-all`;
|
|
459
|
+
// Optional bounded abort (see `RefreshAllOptions.timeout`). A positive
|
|
460
|
+
// timeout arms an `AbortController` that aborts the in-flight request; an
|
|
461
|
+
// abort is treated as "no signed-in accounts on this device" — the same
|
|
462
|
+
// outcome as a 401 — so a cross-domain stall falls through cleanly instead
|
|
463
|
+
// of hanging the cold boot.
|
|
464
|
+
const timeout = typeof options.timeout === 'number' && options.timeout > 0 ? options.timeout : undefined;
|
|
465
|
+
const controller = timeout !== undefined ? new AbortController() : undefined;
|
|
466
|
+
const timeoutId = timeout !== undefined && controller
|
|
467
|
+
? setTimeout(() => controller.abort(), timeout)
|
|
468
|
+
: undefined;
|
|
459
469
|
let response;
|
|
460
470
|
try {
|
|
461
471
|
response = await fetch(url, {
|
|
462
472
|
method: 'POST',
|
|
463
473
|
credentials: 'include',
|
|
464
474
|
headers: { Accept: 'application/json' },
|
|
475
|
+
signal: controller?.signal,
|
|
465
476
|
});
|
|
466
477
|
}
|
|
467
478
|
catch (error) {
|
|
479
|
+
// A bounded-timeout abort is the "not signed in / cross-domain stall"
|
|
480
|
+
// path, NOT an error. The browser raises a DOMException named
|
|
481
|
+
// 'AbortError' (some runtimes use a generic Error); match on the name so
|
|
482
|
+
// we never throw the timeout into the cold-boot error handler.
|
|
483
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
484
|
+
return { accounts: [] };
|
|
485
|
+
}
|
|
468
486
|
throw this.handleError(error);
|
|
469
487
|
}
|
|
488
|
+
finally {
|
|
489
|
+
if (timeoutId !== undefined) {
|
|
490
|
+
clearTimeout(timeoutId);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
470
493
|
if (response.status === 401) {
|
|
471
494
|
return { accounts: [] };
|
|
472
495
|
}
|
|
@@ -294,6 +294,20 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
294
294
|
// Optional/interactive mediation should only happen when the user clicks "Sign In".
|
|
295
295
|
let credential = null;
|
|
296
296
|
const loginHint = this.getStoredLoginHint();
|
|
297
|
+
// Fast-skip: with no stored login hint this browser has never completed a
|
|
298
|
+
// FedCM sign-in for any Oxy account, so silent mediation cannot return a
|
|
299
|
+
// credential — the IdP has nothing to silently re-issue. Doing the full
|
|
300
|
+
// round-trip anyway (mint a nonce via `POST /fedcm/nonce`, then a
|
|
301
|
+
// `navigator.credentials.get` that aborts after `FEDCM_SILENT_TIMEOUT`) is
|
|
302
|
+
// pure latency in the cold-boot critical path. Return `null` immediately so
|
|
303
|
+
// the next cold-boot step (stored-session / iframe / bounce) runs without
|
|
304
|
+
// the wasted nonce mint + abort wait. A genuinely associated browser always
|
|
305
|
+
// has a hint (it is stored only after a real exchange), so this never skips
|
|
306
|
+
// a recoverable session.
|
|
307
|
+
if (!loginHint) {
|
|
308
|
+
debug.log('Silent SSO: No stored login hint — skipping silent mediation (no association on this browser)');
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
297
311
|
try {
|
|
298
312
|
// Server-minted, origin-bound nonce — required for `/fedcm/exchange`
|
|
299
313
|
// to accept the resulting ID token (anti-replay binding).
|
|
@@ -741,12 +755,17 @@ function OxyServicesFedCMMixin(Base) {
|
|
|
741
755
|
_a.DEFAULT_CONFIG_URL = 'https://auth.oxy.so/fedcm.json',
|
|
742
756
|
_a.FEDCM_TIMEOUT = 15000 // 15 seconds for interactive
|
|
743
757
|
,
|
|
744
|
-
// Silent mediation runs on page load
|
|
745
|
-
//
|
|
746
|
-
// round-trip
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
|
|
758
|
+
// Silent mediation runs on page load as ONE step of the ordered cold-boot
|
|
759
|
+
// sequence (mint nonce → navigator.credentials.get → /fedcm/exchange). The
|
|
760
|
+
// real round-trip was measured at >3s for live users, so the budget must stay
|
|
761
|
+
// comfortably above 3s. It must ALSO be tight: on a logged-out browser this
|
|
762
|
+
// step never resolves a credential, and every millisecond it spends timing
|
|
763
|
+
// out is pure latency in front of the steps that actually hold the answer
|
|
764
|
+
// (stored-session bearer, the per-apex silent iframe, the /sso bounce). 4s is
|
|
765
|
+
// the floor that preserves the >3s success margin while bounding the dead
|
|
766
|
+
// wait — down from the previous 10s, which alone could account for most of a
|
|
767
|
+
// 20-30s cold-boot stall. Do NOT lower below 4s (it would clip live success).
|
|
768
|
+
_a.FEDCM_SILENT_TIMEOUT = 4000 // 4 seconds for silent mediation
|
|
750
769
|
,
|
|
751
770
|
_a;
|
|
752
771
|
}
|
|
@@ -411,8 +411,24 @@ function OxyServicesPopupAuthMixin(Base) {
|
|
|
411
411
|
cleanup();
|
|
412
412
|
resolve(session || null);
|
|
413
413
|
};
|
|
414
|
+
// Fail-fast on a load failure. When the per-apex `/auth/silent` host is
|
|
415
|
+
// unreachable, blocked by CSP `frame-ancestors`/`X-Frame-Options`, or the
|
|
416
|
+
// network drops, the iframe never posts a message — without this handler
|
|
417
|
+
// the silent restore would block for the FULL `timeout` (dead latency in
|
|
418
|
+
// the cold-boot critical path). `onerror`/`onabort` fire on a failed load,
|
|
419
|
+
// so resolve `null` immediately and let the next cold-boot step run. The
|
|
420
|
+
// success path posts a message and is handled above; these only catch the
|
|
421
|
+
// no-message failure modes.
|
|
422
|
+
const failFast = () => {
|
|
423
|
+
cleanup();
|
|
424
|
+
resolve(null);
|
|
425
|
+
};
|
|
426
|
+
iframe.onerror = failFast;
|
|
427
|
+
iframe.onabort = failFast;
|
|
414
428
|
const cleanup = () => {
|
|
415
429
|
clearTimeout(timeoutId);
|
|
430
|
+
iframe.onerror = null;
|
|
431
|
+
iframe.onabort = null;
|
|
416
432
|
window.removeEventListener('message', messageHandler);
|
|
417
433
|
};
|
|
418
434
|
window.addEventListener('message', messageHandler);
|