@oxyhq/core 5.5.0 → 7.0.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/HttpService.js +7 -4
- package/dist/cjs/OxyServices.base.js +7 -102
- package/dist/cjs/boot/coldBootV2.js +350 -0
- package/dist/cjs/boot/deviceBootReturn.js +152 -0
- package/dist/cjs/crypto/keyManager.js +95 -0
- package/dist/cjs/i18n/locales/en-US.json +13 -1
- package/dist/cjs/i18n/locales/es-ES.json +13 -1
- package/dist/cjs/i18n/locales/locales/en-US.json +13 -1
- package/dist/cjs/i18n/locales/locales/es-ES.json +13 -1
- package/dist/cjs/index.js +54 -45
- package/dist/cjs/mixins/OxyServices.accounts.js +13 -26
- package/dist/cjs/mixins/OxyServices.auth.js +66 -201
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +119 -0
- package/dist/cjs/mixins/index.js +8 -17
- package/dist/cjs/server/index.js +2 -2
- package/dist/cjs/session/SessionClient.js +40 -1
- package/dist/cjs/session/authStateStore.js +284 -0
- package/dist/cjs/session/refresh.js +264 -0
- package/dist/cjs/shared/utils/debugUtils.js +3 -3
- package/dist/cjs/utils/accountUtils.js +1 -55
- package/dist/cjs/utils/authWebUrl.js +10 -39
- package/dist/cjs/utils/coldBoot.js +10 -8
- package/dist/cjs/utils/platform.js +19 -0
- package/dist/cjs/utils/registrableApex.js +49 -0
- package/dist/cjs/utils/ssoBounce.js +15 -362
- package/dist/cjs/utils/validationUtils.js +57 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +7 -4
- package/dist/esm/OxyServices.base.js +7 -102
- package/dist/esm/boot/coldBootV2.js +344 -0
- package/dist/esm/boot/deviceBootReturn.js +146 -0
- package/dist/esm/crypto/keyManager.js +95 -0
- package/dist/esm/i18n/locales/en-US.json +13 -1
- package/dist/esm/i18n/locales/es-ES.json +13 -1
- package/dist/esm/i18n/locales/locales/en-US.json +13 -1
- package/dist/esm/i18n/locales/locales/es-ES.json +13 -1
- package/dist/esm/index.js +34 -17
- package/dist/esm/mixins/OxyServices.accounts.js +13 -26
- package/dist/esm/mixins/OxyServices.auth.js +66 -201
- package/dist/esm/mixins/OxyServices.deviceBoot.js +116 -0
- package/dist/esm/mixins/index.js +8 -17
- package/dist/esm/server/index.js +1 -1
- package/dist/esm/session/SessionClient.js +40 -1
- package/dist/esm/session/authStateStore.js +278 -0
- package/dist/esm/session/refresh.js +257 -0
- package/dist/esm/shared/utils/debugUtils.js +3 -3
- package/dist/esm/utils/accountUtils.js +0 -53
- package/dist/esm/utils/authWebUrl.js +9 -37
- package/dist/esm/utils/coldBoot.js +10 -8
- package/dist/esm/utils/platform.js +18 -0
- package/dist/esm/utils/registrableApex.js +46 -0
- package/dist/esm/utils/ssoBounce.js +14 -345
- package/dist/esm/utils/validationUtils.js +56 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +14 -1
- package/dist/types/OxyServices.base.d.ts +0 -52
- package/dist/types/OxyServices.d.ts +0 -25
- package/dist/types/boot/coldBootV2.d.ts +76 -0
- package/dist/types/boot/deviceBootReturn.d.ts +83 -0
- package/dist/types/crypto/keyManager.d.ts +21 -0
- package/dist/types/index.d.ts +14 -21
- package/dist/types/mixins/OxyServices.accounts.d.ts +24 -29
- package/dist/types/mixins/OxyServices.analytics.d.ts +0 -2
- package/dist/types/mixins/OxyServices.appData.d.ts +0 -2
- package/dist/types/mixins/OxyServices.assets.d.ts +0 -2
- package/dist/types/mixins/OxyServices.auth.d.ts +35 -77
- package/dist/types/mixins/OxyServices.civic.d.ts +0 -2
- package/dist/types/mixins/OxyServices.connectedApps.d.ts +0 -2
- package/dist/types/mixins/OxyServices.contacts.d.ts +0 -2
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +110 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +0 -2
- package/dist/types/mixins/OxyServices.features.d.ts +0 -2
- package/dist/types/mixins/OxyServices.identity.d.ts +0 -2
- package/dist/types/mixins/OxyServices.language.d.ts +0 -2
- package/dist/types/mixins/OxyServices.links.d.ts +0 -2
- package/dist/types/mixins/OxyServices.location.d.ts +0 -2
- package/dist/types/mixins/OxyServices.nodes.d.ts +0 -2
- package/dist/types/mixins/OxyServices.payment.d.ts +0 -2
- package/dist/types/mixins/OxyServices.privacy.d.ts +0 -2
- package/dist/types/mixins/OxyServices.reputation.d.ts +0 -2
- package/dist/types/mixins/OxyServices.security.d.ts +0 -2
- package/dist/types/mixins/OxyServices.topics.d.ts +0 -2
- package/dist/types/mixins/OxyServices.user.d.ts +0 -2
- package/dist/types/mixins/OxyServices.utility.d.ts +0 -2
- package/dist/types/mixins/index.d.ts +5 -9
- package/dist/types/models/interfaces.d.ts +0 -67
- package/dist/types/models/session.d.ts +4 -5
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/session/SessionClient.d.ts +26 -1
- package/dist/types/session/authStateStore.d.ts +119 -0
- package/dist/types/session/refresh.d.ts +93 -0
- package/dist/types/shared/utils/debugUtils.d.ts +3 -3
- package/dist/types/utils/accountUtils.d.ts +2 -17
- package/dist/types/utils/authWebUrl.d.ts +9 -35
- package/dist/types/utils/coldBoot.d.ts +17 -14
- package/dist/types/utils/platform.d.ts +14 -0
- package/dist/types/utils/registrableApex.d.ts +31 -0
- package/dist/types/utils/ssoBounce.d.ts +14 -280
- package/dist/types/utils/validationUtils.d.ts +15 -0
- package/package.json +2 -2
- package/src/HttpService.ts +20 -4
- package/src/OxyServices.base.ts +7 -112
- package/src/OxyServices.ts +0 -38
- package/src/boot/__tests__/coldBootV2.test.ts +317 -0
- package/src/boot/__tests__/deviceBootReturn.test.ts +158 -0
- package/src/boot/coldBootV2.ts +426 -0
- package/src/boot/deviceBootReturn.ts +195 -0
- package/src/crypto/__tests__/sharedDeviceToken.test.ts +24 -0
- package/src/crypto/keyManager.ts +101 -0
- package/src/i18n/locales/en-US.json +13 -1
- package/src/i18n/locales/es-ES.json +13 -1
- package/src/index.ts +75 -65
- package/src/mixins/OxyServices.accounts.ts +27 -39
- package/src/mixins/OxyServices.auth.ts +78 -253
- package/src/mixins/OxyServices.deviceBoot.ts +146 -0
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +107 -0
- package/src/mixins/__tests__/accounts.test.ts +17 -44
- package/src/mixins/__tests__/passwordSignIn.test.ts +91 -0
- package/src/mixins/index.ts +10 -23
- package/src/models/interfaces.ts +0 -79
- package/src/models/session.ts +4 -5
- package/src/server/index.ts +1 -1
- package/src/session/SessionClient.ts +53 -2
- package/src/session/__tests__/SessionClient.additive.test.ts +92 -0
- package/src/session/__tests__/SessionClient.rest.test.ts +25 -0
- package/src/session/__tests__/SessionClient.state.test.ts +18 -5
- package/src/session/__tests__/authStateStore.test.ts +209 -0
- package/src/session/__tests__/refresh.test.ts +256 -0
- package/src/session/authStateStore.ts +335 -0
- package/src/session/refresh.ts +334 -0
- package/src/shared/utils/debugUtils.ts +3 -3
- package/src/utils/__tests__/authWebUrl.test.ts +5 -40
- package/src/utils/__tests__/registrableApex.test.ts +62 -0
- package/src/utils/__tests__/validationUtils.test.ts +30 -0
- package/src/utils/accountUtils.ts +2 -65
- package/src/utils/authWebUrl.ts +9 -39
- package/src/utils/coldBoot.ts +17 -14
- package/src/utils/platform.ts +21 -0
- package/src/utils/registrableApex.ts +45 -0
- package/src/utils/ssoBounce.ts +14 -393
- package/src/utils/validationUtils.ts +62 -0
- package/dist/cjs/AuthManager.js +0 -1110
- package/dist/cjs/AuthManagerTypes.js +0 -13
- package/dist/cjs/CrossDomainAuth.js +0 -206
- package/dist/cjs/mixins/OxyServices.fedcm.js +0 -823
- package/dist/cjs/mixins/OxyServices.redirect.js +0 -95
- package/dist/cjs/mixins/OxyServices.silent.js +0 -204
- package/dist/cjs/mixins/OxyServices.sso.js +0 -208
- package/dist/cjs/utils/fapiAutoDetect.js +0 -99
- package/dist/cjs/utils/ssoEstablish.js +0 -110
- package/dist/cjs/utils/ssoReturn.js +0 -275
- package/dist/esm/AuthManager.js +0 -1105
- package/dist/esm/AuthManagerTypes.js +0 -12
- package/dist/esm/CrossDomainAuth.js +0 -201
- package/dist/esm/mixins/OxyServices.fedcm.js +0 -821
- package/dist/esm/mixins/OxyServices.redirect.js +0 -92
- package/dist/esm/mixins/OxyServices.silent.js +0 -202
- package/dist/esm/mixins/OxyServices.sso.js +0 -204
- package/dist/esm/utils/fapiAutoDetect.js +0 -95
- package/dist/esm/utils/ssoEstablish.js +0 -107
- package/dist/esm/utils/ssoReturn.js +0 -271
- package/dist/types/AuthManager.d.ts +0 -380
- package/dist/types/AuthManagerTypes.d.ts +0 -81
- package/dist/types/CrossDomainAuth.d.ts +0 -164
- package/dist/types/mixins/OxyServices.fedcm.d.ts +0 -331
- package/dist/types/mixins/OxyServices.redirect.d.ts +0 -92
- package/dist/types/mixins/OxyServices.silent.d.ts +0 -132
- package/dist/types/mixins/OxyServices.sso.d.ts +0 -138
- package/dist/types/utils/fapiAutoDetect.d.ts +0 -56
- package/dist/types/utils/ssoEstablish.d.ts +0 -85
- package/dist/types/utils/ssoReturn.d.ts +0 -156
- package/src/AuthManager.ts +0 -1269
- package/src/AuthManagerTypes.ts +0 -86
- package/src/CrossDomainAuth.ts +0 -243
- package/src/__tests__/authManager.cookiePath.test.ts +0 -390
- package/src/__tests__/authManager.security.test.ts +0 -377
- package/src/__tests__/crossDomainAuth.test.ts +0 -116
- package/src/__tests__/establishDeviceRefreshSlot.test.ts +0 -221
- package/src/mixins/OxyServices.fedcm.ts +0 -1026
- package/src/mixins/OxyServices.redirect.ts +0 -122
- package/src/mixins/OxyServices.silent.ts +0 -272
- package/src/mixins/OxyServices.sso.ts +0 -261
- package/src/mixins/__tests__/constructorAuthWebUrl.test.ts +0 -85
- package/src/mixins/__tests__/fedcm.test.ts +0 -667
- package/src/mixins/__tests__/sessionBaseUrl.test.ts +0 -61
- package/src/mixins/__tests__/silent.test.ts +0 -102
- package/src/mixins/__tests__/sso.test.ts +0 -228
- package/src/utils/__tests__/consumeSsoReturn.test.ts +0 -816
- package/src/utils/__tests__/fapiAutoDetect.test.ts +0 -183
- package/src/utils/__tests__/ssoBounce.test.ts +0 -219
- package/src/utils/__tests__/ssoEstablish.test.ts +0 -204
- package/src/utils/__tests__/ssoReturn.test.ts +0 -276
- package/src/utils/fapiAutoDetect.ts +0 -91
- package/src/utils/ssoEstablish.ts +0 -174
- package/src/utils/ssoReturn.ts +0 -389
|
@@ -1,287 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* URL builder, and the small pure predicates shared by every consumer's
|
|
4
|
-
* cold-boot `sso-return` / `sso-bounce` steps and bfcache `pageshow`
|
|
5
|
-
* re-evaluation.
|
|
2
|
+
* SSO callback path constant.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The client SSO-bounce machinery (per-origin sessionStorage keys, the bounce
|
|
5
|
+
* URL builder, the `guardActive` / `allowSsoBounce` / `getSsoCallbackBootstrapScript`
|
|
6
|
+
* predicates, …) was removed in the device-first cutover — RP apps no longer
|
|
7
|
+
* bounce through `auth.oxy.so`. The one survivor is this path constant, still
|
|
8
|
+
* referenced by the api SSO controller and the IdP (both lista B, gated on the
|
|
9
|
+
* ecosystem bump). `@oxyhq/core/server` re-exports it for the api.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `auth.oxy.so/sso?prompt=none&client_id=<origin>&return_to=<origin>{@link SSO_CALLBACK_PATH}&state=<s>`.
|
|
16
|
-
* Before navigating it records, in this origin's `sessionStorage`, the
|
|
17
|
-
* CSRF `state` ({@link ssoStateKey}), a guard timestamp ({@link ssoGuardKey},
|
|
18
|
-
* the loop breaker), and the real destination URL ({@link ssoDestKey}) to
|
|
19
|
-
* restore after the callback.
|
|
20
|
-
* 2. The central IdP worker reads its first-party `fedcm_session`, mints a
|
|
21
|
-
* session, stores it under an opaque single-use `code`, and 303-redirects
|
|
22
|
-
* back to `<origin>{@link SSO_CALLBACK_PATH}#oxy_sso=ok&code=<code>&state=<s>`
|
|
23
|
-
* (or `#oxy_sso=none` / `#oxy_sso=error`).
|
|
24
|
-
* 3. `sso-return` parses the fragment (`parseSsoReturnFragment`), validates
|
|
25
|
-
* `state`, exchanges the `code` via `oxyServices.exchangeSsoCode`, commits
|
|
26
|
-
* the session, then restores the original destination.
|
|
27
|
-
*
|
|
28
|
-
* Loop proof (logged-out): first load all steps skip → `sso-bounce` sets
|
|
29
|
-
* guard/state/dest + the outcome-independent attempted-flag
|
|
30
|
-
* ({@link ssoAttemptedKey}) and navigates; the IdP (no central session) returns
|
|
31
|
-
* `#oxy_sso=none`; the callback load's `sso-return` sees `none`, sets the
|
|
32
|
-
* NO_SESSION flag ({@link ssoNoSessionKey}), and `sso-bounce` is then disabled.
|
|
33
|
-
* Exactly ONE bounce, no loop. An interrupted bounce (user hit back
|
|
34
|
-
* mid-redirect) self-heals once the {@link SSO_GUARD_TTL_MS} guard TTL lapses.
|
|
35
|
-
* The attempted-flag is the definitive, outcome-INDEPENDENT loop breaker: it is
|
|
36
|
-
* set pre-bounce so even if the return-side NO_SESSION write never lands, the
|
|
37
|
-
* bounce can never re-fire this tab after the self-heal TTL lapses.
|
|
38
|
-
*
|
|
39
|
-
* All state lives in `sessionStorage` (per tab, cleared on tab close) and is
|
|
40
|
-
* keyed per-origin so two RPs hosted in the same browser never collide. The
|
|
41
|
-
* key strings and the 30s TTL are a wire/storage contract — they MUST match
|
|
42
|
-
* the values the IdP and every consumer expect and must not change lightly.
|
|
11
|
+
* LEGACY(old-sdk): `SSO_CALLBACK_PATH` survives ONLY for the lista-B api/IdP SSO
|
|
12
|
+
* surface. Deletable once Homiio/Allo/Alia/Syra are bumped off the old SDK AND
|
|
13
|
+
* CloudWatch `/oxy/ecs` shows the `/sso*` + `/fedcm/*` routes quiet — the
|
|
14
|
+
* F-final sweep should remove this file then.
|
|
43
15
|
*/
|
|
44
16
|
/**
|
|
45
|
-
* The RP callback path the central IdP redirects back to after a
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* {@link ssoDestKey}), so the user never lingers on this internal path.
|
|
17
|
+
* The RP callback path the central IdP redirects back to after a legacy SSO
|
|
18
|
+
* bounce. Kept as the single source of truth so the api/IdP never hardcode the
|
|
19
|
+
* literal.
|
|
49
20
|
*/
|
|
50
21
|
export declare const SSO_CALLBACK_PATH = "/__oxy/sso-callback";
|
|
51
|
-
/**
|
|
52
|
-
* Self-healing TTL (ms) for the bounce guard. An in-flight bounce sets a
|
|
53
|
-
* timestamp guard; if the bounce is interrupted before the callback lands
|
|
54
|
-
* (e.g. the user navigates back mid-redirect), the guard would otherwise pin
|
|
55
|
-
* the RP signed-out forever. After this window the guard is treated as stale
|
|
56
|
-
* and a fresh single bounce is permitted. 30s comfortably exceeds a real
|
|
57
|
-
* redirect round-trip while keeping a crash short-lived.
|
|
58
|
-
*/
|
|
59
|
-
export declare const SSO_GUARD_TTL_MS = 30000;
|
|
60
|
-
/** Per-origin CSRF state key (matched on return to defeat fragment forgery). */
|
|
61
|
-
export declare function ssoStateKey(origin: string): string;
|
|
62
|
-
/** Per-origin bounce guard key (a timestamp; loop breaker + self-heal TTL). */
|
|
63
|
-
export declare function ssoGuardKey(origin: string): string;
|
|
64
|
-
/** Per-origin destination key (the real URL to restore after the callback). */
|
|
65
|
-
export declare function ssoDestKey(origin: string): string;
|
|
66
|
-
/**
|
|
67
|
-
* Per-origin "the central IdP has no session for me" key. Set after a
|
|
68
|
-
* `none`/`error` return (or a failed/forged exchange) so `sso-bounce` does not
|
|
69
|
-
* fire again this tab — the definitive loop breaker.
|
|
70
|
-
*/
|
|
71
|
-
export declare function ssoNoSessionKey(origin: string): string;
|
|
72
|
-
/**
|
|
73
|
-
* Per-origin, OUTCOME-INDEPENDENT once-guard. Set in `sessionStorage` BEFORE
|
|
74
|
-
* the terminal SSO bounce navigates. Gates the bounce so the silent
|
|
75
|
-
* cross-domain probe fires AT MOST ONCE per tab session — independent of
|
|
76
|
-
* whether the return-side NO_SESSION flag ever lands. The definitive loop
|
|
77
|
-
* breaker; survives the 30s self-heal `ssoGuardKey` TTL. Cleared only on an
|
|
78
|
-
* explicit sign-out/clear so a later cold boot (after the user signs in
|
|
79
|
-
* centrally) can probe again.
|
|
80
|
-
*/
|
|
81
|
-
export declare function ssoAttemptedKey(origin: string): string;
|
|
82
|
-
/**
|
|
83
|
-
* Per-origin DURABLE "this device/origin has had a signed-in Oxy session
|
|
84
|
-
* before" hint.
|
|
85
|
-
*
|
|
86
|
-
* Unlike every other key in this module — which lives in per-tab
|
|
87
|
-
* `sessionStorage` — this hint is written to DURABLE storage (web
|
|
88
|
-
* `localStorage`; the services provider uses its own `storageKeyPrefix`-scoped
|
|
89
|
-
* key in `@oxyhq/services`). It is set whenever a session is established or
|
|
90
|
-
* restored and survives a session expiring; it is cleared ONLY on an explicit
|
|
91
|
-
* full sign-out. It exists purely to drive {@link allowSsoBounce}: a returning
|
|
92
|
-
* visitor (hint present) whose local session has lapsed still gets ONE terminal
|
|
93
|
-
* `/sso` establish bounce to recover a session that lives only at the central
|
|
94
|
-
* IdP, while a truly first-time anonymous visitor is never force-bounced.
|
|
95
|
-
*/
|
|
96
|
-
export declare function ssoPriorSessionKey(origin: string): string;
|
|
97
|
-
/**
|
|
98
|
-
* Per-origin DURABLE "the user DELIBERATELY signed out on this device/origin"
|
|
99
|
-
* flag.
|
|
100
|
-
*
|
|
101
|
-
* Like {@link ssoPriorSessionKey} this lives in DURABLE storage (web
|
|
102
|
-
* `localStorage`; services uses its own `storageKeyPrefix`-scoped key), NOT the
|
|
103
|
-
* per-tab `sessionStorage` the loop-breaker keys use — it must survive a reload.
|
|
104
|
-
*
|
|
105
|
-
* It exists purely to suppress AUTOMATIC silent restore after a deliberate
|
|
106
|
-
* sign-out: a still-live IdP session (the central `fedcm_session`) would
|
|
107
|
-
* otherwise let the per-apex `/auth/silent` iframe re-mint a session on the
|
|
108
|
-
* very next cold boot, so a user who pressed "Sign out" gets silently signed
|
|
109
|
-
* back in on reload. With this flag set, that silent cold-boot step is
|
|
110
|
-
* skipped while the Gmail-style returning-account fast-path is otherwise
|
|
111
|
-
* preserved.
|
|
112
|
-
*
|
|
113
|
-
* Lifecycle (mirrors the existing gate machinery — set on a definitive event,
|
|
114
|
-
* cleared on its inverse):
|
|
115
|
-
* - SET on EXPLICIT full sign-out (alongside clearing the prior-session hint
|
|
116
|
-
* and the SSO bounce state).
|
|
117
|
-
* - CLEARED on ANY deliberate sign-in (password, account switch, device
|
|
118
|
-
* claim) so a real sign-in fully re-enables silent restore — there is no
|
|
119
|
-
* "stuck signed out" state.
|
|
120
|
-
*
|
|
121
|
-
* NOTE: this gates only AUTOMATIC/silent restore. An INTERACTIVE sign-in always
|
|
122
|
-
* clears it first, so the user can always sign back in.
|
|
123
|
-
*/
|
|
124
|
-
export declare function ssoSignedOutKey(origin: string): string;
|
|
125
|
-
/**
|
|
126
|
-
* Per-origin key holding the LAST consumed SSO-return outcome (`ok` | `none` |
|
|
127
|
-
* `error`, plus an optional machine-readable `reason` on the non-`ok` outcomes).
|
|
128
|
-
*
|
|
129
|
-
* Lives in per-tab `sessionStorage` like the other loop-breaker keys, and for
|
|
130
|
-
* the same reason: a `none`/`error` return HARD-navigates the RP off the
|
|
131
|
-
* internal callback path back to its real destination (a fresh document load),
|
|
132
|
-
* so the outcome an RP wants to render ("the central IdP had no session — show a
|
|
133
|
-
* branded sign-in screen instead of bouncing again") must survive that
|
|
134
|
-
* round-trip. The RP reads it on the destination load to decide whether an
|
|
135
|
-
* AUTOMATIC (guard-driven) sign-in should re-bounce or defer to a user gesture.
|
|
136
|
-
*
|
|
137
|
-
* Written as a small JSON blob (`{kind, reason?}`). Set whenever a return is
|
|
138
|
-
* consumed; cleared on a successful session commit and on an explicit
|
|
139
|
-
* user-gesture sign-in / full sign-out (so a deliberate retry is never
|
|
140
|
-
* suppressed by a prior automatic none/error).
|
|
141
|
-
*/
|
|
142
|
-
export declare function ssoOutcomeKey(origin: string): string;
|
|
143
|
-
/**
|
|
144
|
-
* Per-origin marker written by the pre-hydration callback bootstrap.
|
|
145
|
-
*
|
|
146
|
-
* Static Expo exports render unknown paths as `+not-found`; on
|
|
147
|
-
* `/__oxy/sso-callback` that can fail hydration before the React provider has a
|
|
148
|
-
* chance to run `consumeSsoReturn`. The bootstrap runs in the HTML head, moves
|
|
149
|
-
* the URL to a hydratable route while preserving the SSO fragment, and writes
|
|
150
|
-
* this marker so `consumeSsoReturn` still restores the original destination as
|
|
151
|
-
* if the page were physically on the callback path.
|
|
152
|
-
*/
|
|
153
|
-
export declare function ssoCallbackBootstrapKey(origin: string): string;
|
|
154
|
-
/**
|
|
155
|
-
* Inline script for Expo/static web apps.
|
|
156
|
-
*
|
|
157
|
-
* Must run before the app bundle hydrates. It is intentionally tiny and
|
|
158
|
-
* dependency-free: if the browser lands on the internal callback route with an
|
|
159
|
-
* Oxy SSO fragment, it marks the handoff and rewrites the path to `/` while
|
|
160
|
-
* preserving `#oxy_sso=...`. The normal SDK cold-boot `sso-return` step then
|
|
161
|
-
* consumes the fragment from a route that can hydrate. If the internal route is
|
|
162
|
-
* reached without a valid SSO fragment, it leaves the route via a hard root
|
|
163
|
-
* navigation because there is no session material to preserve.
|
|
164
|
-
*/
|
|
165
|
-
export declare function getSsoCallbackBootstrapScript(): string;
|
|
166
|
-
/**
|
|
167
|
-
* Perform the terminal top-level SSO bounce navigation.
|
|
168
|
-
*
|
|
169
|
-
* A thin wrapper over `window.location.assign(url)` so the single navigation
|
|
170
|
-
* seam lives in one place (and stays mockable in tests, where jsdom's
|
|
171
|
-
* `Location.assign` is a non-configurable native method). In production this is
|
|
172
|
-
* exactly `window.location.assign` — the document is torn down and replaced by
|
|
173
|
-
* the central IdP page. Off-browser (SSR / native) it is a no-op: native never
|
|
174
|
-
* bounces.
|
|
175
|
-
*/
|
|
176
|
-
export declare function ssoNavigate(url: string): void;
|
|
177
|
-
/**
|
|
178
|
-
* Build the central IdP `/sso` bounce URL for an RP.
|
|
179
|
-
*
|
|
180
|
-
* Pure (no DOM access) so it is unit-testable and shared by every consumer's
|
|
181
|
-
* terminal `sso-bounce` step. The IdP reads `client_id` (the RP origin) and
|
|
182
|
-
* `return_to` to mint an origin-bound opaque code and 303-redirect back.
|
|
183
|
-
*
|
|
184
|
-
* The IdP base is resolved via {@link resolveCentralAuthUrl} so an explicit
|
|
185
|
-
* `authWebUrl` override (e.g. a staging IdP) drives the SSO bounce exactly the
|
|
186
|
-
* way it drives FedCM. When omitted, the central default {@link CENTRAL_AUTH_URL}
|
|
187
|
-
* is used.
|
|
188
|
-
*
|
|
189
|
-
* @param origin - The RP origin (`window.location.origin`).
|
|
190
|
-
* @param state - The CSRF state minted for this bounce.
|
|
191
|
-
* @param authWebUrl - Optional explicit IdP base URL override. Falls back to
|
|
192
|
-
* the central default when `undefined`/empty.
|
|
193
|
-
* @returns The absolute `<idp-origin>/sso?...` URL string.
|
|
194
|
-
*/
|
|
195
|
-
export declare function buildSsoBounceUrl(origin: string, state: string, authWebUrl?: string): string;
|
|
196
|
-
/**
|
|
197
|
-
* Whether `origin` IS the central IdP origin. The RP must NEVER bounce while
|
|
198
|
-
* sitting on `auth.oxy.so` itself — doing so would loop the IdP against itself.
|
|
199
|
-
*
|
|
200
|
-
* Both sides are normalised via `new URL(...).origin` so a trailing-slash or
|
|
201
|
-
* path difference never defeats the guard. Returns `false` on any parse
|
|
202
|
-
* failure (an unparseable candidate is, by definition, not the central IdP).
|
|
203
|
-
*/
|
|
204
|
-
export declare function isCentralIdPOrigin(origin: string): boolean;
|
|
205
|
-
/**
|
|
206
|
-
* Read the bounce guard and decide whether it is still ACTIVE.
|
|
207
|
-
*
|
|
208
|
-
* Active means: a guard value is present AND it parses to a finite timestamp
|
|
209
|
-
* AND less than {@link SSO_GUARD_TTL_MS} has elapsed since it was set. An active
|
|
210
|
-
* guard disables `sso-bounce` (a bounce is already in flight this tab). A
|
|
211
|
-
* missing, malformed, or expired guard is NOT active, so a fresh bounce may
|
|
212
|
-
* proceed (this is the 30s self-heal for an interrupted bounce).
|
|
213
|
-
*
|
|
214
|
-
* Defensive: a `getItem` that throws (e.g. a locked/disabled storage) is
|
|
215
|
-
* treated as "not active" so the guard never wedges the flow.
|
|
216
|
-
*
|
|
217
|
-
* @param storage - The session storage to read (injected for testability).
|
|
218
|
-
* @param origin - The page origin whose guard to evaluate.
|
|
219
|
-
* @param now - Current epoch ms (injected for deterministic tests). Defaults to
|
|
220
|
-
* `Date.now()`.
|
|
221
|
-
*/
|
|
222
|
-
export declare function guardActive(storage: Pick<Storage, 'getItem'>, origin: string, now?: number): boolean;
|
|
223
|
-
/**
|
|
224
|
-
* Whether AUTOMATIC silent restore is SUPPRESSED for this origin because the
|
|
225
|
-
* user deliberately signed out (the durable {@link ssoSignedOutKey} flag).
|
|
226
|
-
*
|
|
227
|
-
* When `true`, the silent cold-boot step that can re-mint a session from a
|
|
228
|
-
* still-live IdP session WITHOUT user intent — the per-apex
|
|
229
|
-
* `/auth/silent` iframe — MUST be skipped, so a user who pressed "Sign out" is
|
|
230
|
-
* not silently signed back in on the next reload. Interactive sign-in clears the
|
|
231
|
-
* flag, so this never blocks a deliberate re-sign-in.
|
|
232
|
-
*
|
|
233
|
-
* Defensive: a `getItem` that throws (locked/disabled storage) is treated as NOT
|
|
234
|
-
* suppressed, so the gate fails toward the normal restore behaviour rather than
|
|
235
|
-
* wedging the user out.
|
|
236
|
-
*
|
|
237
|
-
* @param storage - The DURABLE storage to read (web `localStorage`; injected for
|
|
238
|
-
* testability).
|
|
239
|
-
* @param origin - The page origin whose signed-out flag to evaluate.
|
|
240
|
-
*/
|
|
241
|
-
export declare function silentRestoreSuppressed(storage: Pick<Storage, 'getItem'>, origin: string): boolean;
|
|
242
|
-
/**
|
|
243
|
-
* Inputs to the smart {@link allowSsoBounce} gate.
|
|
244
|
-
*/
|
|
245
|
-
export interface SsoBounceGate {
|
|
246
|
-
/**
|
|
247
|
-
* Whether this device/origin has had a signed-in Oxy session before (the
|
|
248
|
-
* durable {@link ssoPriorSessionKey} hint). Set whenever a session is
|
|
249
|
-
* established or restored; survives session expiry; cleared only on explicit
|
|
250
|
-
* full sign-out. `true` ⇒ a returning visitor.
|
|
251
|
-
*/
|
|
252
|
-
readonly hasPriorSession: boolean;
|
|
253
|
-
/**
|
|
254
|
-
* Whether a local/stored session was recovered earlier this cold boot. At the
|
|
255
|
-
* terminal bounce gate this is effectively always `false` (an earlier step
|
|
256
|
-
* would have won and short-circuited), but it is part of the contract — "no
|
|
257
|
-
* prior hint AND no local session" — so it is passed explicitly for fidelity
|
|
258
|
-
* and robustness.
|
|
259
|
-
*/
|
|
260
|
-
readonly hasLocalSession: boolean;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Decide whether the terminal `/sso` establish-bounce is ALLOWED for this
|
|
264
|
-
* visitor (the smart `enabled` gate for the `sso-bounce` cold-boot step).
|
|
265
|
-
*
|
|
266
|
-
* The terminal bounce is the ONLY cold-boot step that can recover a session
|
|
267
|
-
* that lives SOLELY at the central IdP — the cross-apex Relying-Party case
|
|
268
|
-
* (e.g. `mention.earth`, a different apex from `oxy.so`) whose device-local
|
|
269
|
-
* session has expired and whose `Domain=oxy.so` refresh cookie never reaches
|
|
270
|
-
* `api.<apex>`. It is also what plants the first-party per-apex `fedcm_session`
|
|
271
|
-
* cookie that the EARLIER `silent-iframe` step later relies on. So it must fire
|
|
272
|
-
* for a RETURNING user, yet it must NOT force a truly first-time anonymous
|
|
273
|
-
* visitor off to the IdP.
|
|
274
|
-
*
|
|
275
|
-
* - ALLOW when there is a prior-signed-in hint OR a local session was
|
|
276
|
-
* recovered this boot (a returning user) — so a central-only cross-domain
|
|
277
|
-
* session recovers via ONE bounce, after which the per-apex cookie is
|
|
278
|
-
* planted and subsequent loads restore silently with no bounce.
|
|
279
|
-
* - else (no hint, no local session) SUPPRESS — a first-time anonymous
|
|
280
|
-
* visitor browses without a forced redirect.
|
|
281
|
-
*
|
|
282
|
-
* This is the smart DEFAULT and the ONLY behaviour: apps never configure it.
|
|
283
|
-
* It is also the GATE DECISION ONLY — callers still apply the per-tab loop
|
|
284
|
-
* guards (`ssoAttemptedKey`, `ssoNoSessionKey`, {@link guardActive}) so an
|
|
285
|
-
* allowed bounce still fires at most once per cold boot.
|
|
286
|
-
*/
|
|
287
|
-
export declare function allowSsoBounce(gate: SsoBounceGate): boolean;
|
|
@@ -25,6 +25,21 @@ export declare function isValidUsername(username: string): boolean;
|
|
|
25
25
|
* Validate password strength
|
|
26
26
|
*/
|
|
27
27
|
export declare function isValidPassword(password: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Whether `raw` already satisfies the display-name policy, i.e. it contains no
|
|
30
|
+
* disallowed characters AND no orphaned combining marks. Used to REJECT native
|
|
31
|
+
* (signup / profile edit) names with a 400 rather than silently stripping them,
|
|
32
|
+
* and to validate inline in the client editor.
|
|
33
|
+
*
|
|
34
|
+
* The orphaned-mark probe runs on the NFC-normalized form so a legitimate
|
|
35
|
+
* decomposed accent (`e`+◌́) — which normalization recomposes into `é` — is NOT
|
|
36
|
+
* rejected, while a lone, base-less mark (e.g. `"༘"`) IS.
|
|
37
|
+
*
|
|
38
|
+
* The function only checks the character set; an empty or whitespace-only string
|
|
39
|
+
* is considered valid (`true`). Call sites that require a non-empty name enforce
|
|
40
|
+
* that separately.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isValidDisplayName(raw: string): boolean;
|
|
28
43
|
/**
|
|
29
44
|
* Validate required string
|
|
30
45
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
96
|
"dependencies": {
|
|
97
|
-
"@oxyhq/contracts": "^0.
|
|
97
|
+
"@oxyhq/contracts": "^0.10.0",
|
|
98
98
|
"@oxyhq/protocol": "^0.1.1",
|
|
99
99
|
"bip39": "^3.1.0",
|
|
100
100
|
"buffer": "^6.0.3",
|
package/src/HttpService.ts
CHANGED
|
@@ -70,6 +70,19 @@ export interface RequestOptions {
|
|
|
70
70
|
signal?: AbortSignal;
|
|
71
71
|
headers?: Record<string, string>;
|
|
72
72
|
responseType?: 'blob';
|
|
73
|
+
/**
|
|
74
|
+
* Skip BOTH the bearer auth header (and its near-expiry preflight refresh)
|
|
75
|
+
* AND the 401-driven auto-refresh/retry for this request.
|
|
76
|
+
*
|
|
77
|
+
* Required for the body-authenticated refresh endpoint (`POST
|
|
78
|
+
* /auth/refresh-token`): it does not need a bearer, and — critically — it is
|
|
79
|
+
* itself invoked from inside the registered `AuthRefreshHandler`. If it went
|
|
80
|
+
* through the normal preflight, `getAuthHeader` would call
|
|
81
|
+
* `refreshAccessToken` while the handler-owning `tokenRefreshPromise` is
|
|
82
|
+
* still in flight and await ITSELF (deadlock). Skipping auth makes the
|
|
83
|
+
* refresh call fully independent of the current (near-expired) bearer.
|
|
84
|
+
*/
|
|
85
|
+
skipAuth?: boolean;
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
interface RequestConfig extends RequestOptions {
|
|
@@ -403,8 +416,11 @@ export class HttpService {
|
|
|
403
416
|
// Build URL with params
|
|
404
417
|
const fullUrl = this.buildURL(url, params);
|
|
405
418
|
|
|
406
|
-
// Get auth token (with auto-refresh)
|
|
407
|
-
|
|
419
|
+
// Get auth token (with auto-refresh). `skipAuth` requests (the
|
|
420
|
+
// body-authenticated refresh endpoint) send NO bearer and skip the
|
|
421
|
+
// near-expiry preflight — see RequestOptions.skipAuth for the deadlock
|
|
422
|
+
// this avoids.
|
|
423
|
+
const authHeader = config.skipAuth ? null : await this.getAuthHeader();
|
|
408
424
|
|
|
409
425
|
// CSRF protects cookie-authenticated browser writes. Bearer-authenticated
|
|
410
426
|
// SDK clients are not vulnerable to ambient-cookie CSRF, and linked app
|
|
@@ -514,7 +530,7 @@ export class HttpService {
|
|
|
514
530
|
// On 401, delegate refresh to AuthManager and retry once before
|
|
515
531
|
// giving up. HttpService deliberately does not know any session
|
|
516
532
|
// routes; the AuthManager is the single session authority.
|
|
517
|
-
if (response.status === 401 && !config._isAuthRetry) {
|
|
533
|
+
if (response.status === 401 && !config._isAuthRetry && !config.skipAuth) {
|
|
518
534
|
const refreshed = await this.refreshAccessToken('response-401');
|
|
519
535
|
if (refreshed) {
|
|
520
536
|
// `deduplicate: false` is REQUIRED on the retry (mirrors the 403
|
|
@@ -804,7 +820,7 @@ export class HttpService {
|
|
|
804
820
|
* `clearCacheByPrefix` sweeps and `clearCacheEntry` base-key matching.
|
|
805
821
|
* The `clearCacheEntry` callsites all pass fixed, dataless logical keys
|
|
806
822
|
* (`GET:/users/<id>`, `GET:/session/user/<sessionId>`,
|
|
807
|
-
* `GET:/
|
|
823
|
+
* `GET:/auth/grants`), so this readable suffix can never be
|
|
808
824
|
* ambiguous with a serialized request body.
|
|
809
825
|
*/
|
|
810
826
|
private static readonly CACHE_IDENTITY_DELIM = ' id=';
|
package/src/OxyServices.base.ts
CHANGED
|
@@ -8,10 +8,6 @@ import type { OxyConfig as OxyConfigBase, ApiError, User } from './models/interf
|
|
|
8
8
|
import { handleHttpError } from './utils/errorUtils';
|
|
9
9
|
import { HttpService, type AuthRefreshReason, type RequestOptions } from './HttpService';
|
|
10
10
|
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
11
|
-
import { resolveCentralAuthUrl } from './utils/authWebUrl';
|
|
12
|
-
import { isWeb } from './utils/platform';
|
|
13
|
-
import { registrableApex } from './utils/fapiAutoDetect';
|
|
14
|
-
import { logger } from './utils/loggerUtils';
|
|
15
11
|
|
|
16
12
|
export interface OxyConfig extends OxyConfigBase {
|
|
17
13
|
cloudURL?: string;
|
|
@@ -44,23 +40,15 @@ export class OxyServicesBase {
|
|
|
44
40
|
throw new Error('OxyConfig is required');
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// per-apex resolution, but it is NOT the constructor default anymore.
|
|
54
|
-
// An explicit `authWebUrl` always wins (we only fill it when absent).
|
|
55
|
-
const resolvedConfig: OxyConfig = config.authWebUrl
|
|
56
|
-
? config
|
|
57
|
-
: { ...config, authWebUrl: resolveCentralAuthUrl(config.authWebUrl) };
|
|
58
|
-
|
|
59
|
-
this.config = resolvedConfig;
|
|
60
|
-
this.cloudURL = resolvedConfig.cloudURL || 'https://cloud.oxy.so';
|
|
43
|
+
// `authWebUrl` is a plain optional config value now (used only for building
|
|
44
|
+
// third-party "Sign in with Oxy" OAuth links). The SDK no longer derives or
|
|
45
|
+
// defaults an IdP host — the device-first cold boot restores sessions from
|
|
46
|
+
// the persisted refresh store, not an `auth.<apex>` bounce.
|
|
47
|
+
this.config = config;
|
|
48
|
+
this.cloudURL = config.cloudURL || 'https://cloud.oxy.so';
|
|
61
49
|
|
|
62
50
|
// Initialize unified HTTP service (handles auth, caching, deduplication, queuing, retry)
|
|
63
|
-
this.httpService = new HttpService(
|
|
51
|
+
this.httpService = new HttpService(config);
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
// Test-only utility to reset tokens on this instance between jest tests
|
|
@@ -99,23 +87,6 @@ export class OxyServicesBase {
|
|
|
99
87
|
return this.httpService.getBaseURL();
|
|
100
88
|
}
|
|
101
89
|
|
|
102
|
-
/**
|
|
103
|
-
* Get the base URL the SDK's first-party session/refresh calls should target.
|
|
104
|
-
*
|
|
105
|
-
* Returns the configured `sessionBaseUrl` when provided, otherwise falls back
|
|
106
|
-
* to the API `baseURL` (`getBaseURL()`). Per the 2026 session architecture
|
|
107
|
-
* (docs/SESSION-ARCHITECTURE.md), non-`oxy.so` apps point this at their own
|
|
108
|
-
* same-site backend (e.g. `https://api.mention.earth`) whose session bridge
|
|
109
|
-
* forwards the user's refresh credential to `api.oxy.so`; `*.oxy.so` apps
|
|
110
|
-
* leave it unset so it resolves to `https://api.oxy.so` and nothing changes.
|
|
111
|
-
*
|
|
112
|
-
* This is additive: it only exposes configuration for `@oxyhq/services` to
|
|
113
|
-
* consume in a later phase. No refresh/auth logic in core reads it yet.
|
|
114
|
-
*/
|
|
115
|
-
public getSessionBaseUrl(): string {
|
|
116
|
-
return this.config.sessionBaseUrl ?? this.getBaseURL();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
90
|
/**
|
|
120
91
|
* Get the HTTP service instance
|
|
121
92
|
* Useful for advanced use cases where direct access to the HTTP service is needed
|
|
@@ -313,82 +284,6 @@ export class OxyServicesBase {
|
|
|
313
284
|
return this.httpService.getAccessToken();
|
|
314
285
|
}
|
|
315
286
|
|
|
316
|
-
/**
|
|
317
|
-
* Register the CURRENTLY-ACTIVE session in the device's first-party
|
|
318
|
-
* multi-account refresh-cookie set by calling `POST /auth/session`.
|
|
319
|
-
*
|
|
320
|
-
* This is the single, shared primitive every web primary-session commit and the
|
|
321
|
-
* account switch use to plant their `oxy_rt_<authuser>` slot. It MUST be a
|
|
322
|
-
* dedicated call to `/auth/session` rather than relying on whichever endpoint
|
|
323
|
-
* established the session: that endpoint is frequently OUTSIDE the cookie's
|
|
324
|
-
* `Path=/auth` scope (`/accounts/:id/switch`) or is a cross-origin/credential-
|
|
325
|
-
* less restore (`/sso/exchange`, the IdP `/auth/silent` postMessage) that cannot
|
|
326
|
-
* set an `api.oxy.so` cookie at all. `/auth/session` runs where the device's
|
|
327
|
-
* existing slots ARE visible, so the server resolves this user's slot (reusing an
|
|
328
|
-
* existing one or allocating a new one) without clobbering a sibling account,
|
|
329
|
-
* mints a fresh access token bound to the same session, and returns the resolved
|
|
330
|
-
* `authuser`.
|
|
331
|
-
*
|
|
332
|
-
* Behaviour:
|
|
333
|
-
* - Requires a planted bearer (the caller must have already installed the
|
|
334
|
-
* session's access token); `/auth/session` derives the session from it.
|
|
335
|
-
* - On success re-plants the rotated access token (so the active token matches
|
|
336
|
-
* the freshly-rotated cookie) and returns the device `authuser` slot.
|
|
337
|
-
* - WEB-ONLY: on native there are no first-party refresh cookies → returns
|
|
338
|
-
* `null`.
|
|
339
|
-
* - FIRST-PARTY-ONLY: the cookie is host-only on the API host with
|
|
340
|
-
* `SameSite=Lax`, so it only sticks when the page is SAME-SITE (same
|
|
341
|
-
* registrable apex) as the API. On a cross-apex RP (`mention.earth` calling
|
|
342
|
-
* `api.oxy.so`) the browser rejects the `Set-Cookie` as a third-party cookie,
|
|
343
|
-
* so a returned slot would be a phantom never enumerated by `refresh-all`.
|
|
344
|
-
* Those RPs durably restore via the per-apex `/auth/silent` iframe + `/sso`
|
|
345
|
-
* bounce, NOT this device set → returns `null` without calling the API.
|
|
346
|
-
* - BEST-EFFORT: a failure (e.g. transient network) never throws — the session
|
|
347
|
-
* stays active in-memory; only its reload durability via the device set is at
|
|
348
|
-
* risk. The caller treats `null` as "not registered in the device set".
|
|
349
|
-
*
|
|
350
|
-
* @returns The resolved device `authuser` slot, or `null` on native / cross-apex
|
|
351
|
-
* / failure.
|
|
352
|
-
*/
|
|
353
|
-
public async establishDeviceRefreshSlot(): Promise<number | null> {
|
|
354
|
-
if (!isWeb()) {
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
if (typeof window !== 'undefined' && window.location?.hostname) {
|
|
358
|
-
const pageApex = registrableApex(window.location.hostname);
|
|
359
|
-
let apiApex: string | null = null;
|
|
360
|
-
try {
|
|
361
|
-
apiApex = registrableApex(new URL(this.getBaseURL()).hostname);
|
|
362
|
-
} catch {
|
|
363
|
-
apiApex = null;
|
|
364
|
-
}
|
|
365
|
-
if (!pageApex || !apiApex || pageApex !== apiApex) {
|
|
366
|
-
return null;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
try {
|
|
370
|
-
const established = await this.makeRequest<{ accessToken?: string; authuser?: number }>(
|
|
371
|
-
'POST',
|
|
372
|
-
'/auth/session',
|
|
373
|
-
undefined,
|
|
374
|
-
{ cache: false },
|
|
375
|
-
);
|
|
376
|
-
// `/auth/session` mints a fresh access token off the same session; re-plant
|
|
377
|
-
// it so the active token matches the rotated cookie.
|
|
378
|
-
if (established?.accessToken) {
|
|
379
|
-
this.setTokens(established.accessToken);
|
|
380
|
-
}
|
|
381
|
-
return typeof established?.authuser === 'number' ? established.authuser : null;
|
|
382
|
-
} catch (error) {
|
|
383
|
-
logger.warn(
|
|
384
|
-
'[OxyServices] Failed to establish device refresh cookie via POST /auth/session; the session is active in-session but may not survive a reload as part of the device account set',
|
|
385
|
-
{ component: 'OxyServices', method: 'establishDeviceRefreshSlot' },
|
|
386
|
-
error,
|
|
387
|
-
);
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
287
|
/**
|
|
393
288
|
* Decode the current access token and return its `exp` claim in SECONDS since
|
|
394
289
|
* the Unix epoch (the raw JWT `exp` unit), or `null` when there is no token,
|
package/src/OxyServices.ts
CHANGED
|
@@ -58,10 +58,6 @@
|
|
|
58
58
|
*/
|
|
59
59
|
import { OxyServicesBase, type LinkedHttpClient, type OxyConfig } from './OxyServices.base';
|
|
60
60
|
import { OxyAuthenticationError, OxyAuthenticationTimeoutError } from './OxyServices.errors';
|
|
61
|
-
import type { SessionLoginResponse } from './models/session';
|
|
62
|
-
import type { FedCMAuthOptions, FedCMConfig } from './mixins/OxyServices.fedcm';
|
|
63
|
-
import type { SilentAuthOptions } from './mixins/OxyServices.silent';
|
|
64
|
-
import type { RedirectAuthOptions } from './mixins/OxyServices.redirect';
|
|
65
61
|
|
|
66
62
|
// Import mixin composition helper
|
|
67
63
|
import { composeOxyServices } from './mixins';
|
|
@@ -111,16 +107,6 @@ const OxyServicesComposed = composeOxyServices();
|
|
|
111
107
|
// We extend the composed constructor directly — its public surface is broadened
|
|
112
108
|
// to the full mixin set via the interface declaration that follows.
|
|
113
109
|
export class OxyServices extends OxyServicesComposed {
|
|
114
|
-
/**
|
|
115
|
-
* FedCM credential-request timeouts (ms). The runtime values are defined on
|
|
116
|
-
* the FedCM mixin and inherited here via `extends`; these `declare` members
|
|
117
|
-
* surface their types to TypeScript without re-emitting (or duplicating) the
|
|
118
|
-
* literals, so consumers/tests can reference `OxyServices.FEDCM_SILENT_TIMEOUT`
|
|
119
|
-
* with full typing.
|
|
120
|
-
*/
|
|
121
|
-
declare static readonly FEDCM_TIMEOUT: number;
|
|
122
|
-
declare static readonly FEDCM_SILENT_TIMEOUT: number;
|
|
123
|
-
|
|
124
110
|
constructor(config: OxyConfig) {
|
|
125
111
|
super(config);
|
|
126
112
|
}
|
|
@@ -133,30 +119,6 @@ export class OxyServices extends OxyServicesComposed {
|
|
|
133
119
|
export interface OxyServices extends InstanceType<ReturnType<typeof composeOxyServices>> {
|
|
134
120
|
createLinkedClient(config: OxyConfig): LinkedHttpClient;
|
|
135
121
|
|
|
136
|
-
// FedCM authentication
|
|
137
|
-
isFedCMSupported(): boolean;
|
|
138
|
-
signInWithFedCM(options?: FedCMAuthOptions): Promise<SessionLoginResponse>;
|
|
139
|
-
silentSignInWithFedCM(): Promise<SessionLoginResponse | null>;
|
|
140
|
-
revokeFedCMCredential(): Promise<void>;
|
|
141
|
-
getFedCMConfig(): FedCMConfig;
|
|
142
|
-
|
|
143
|
-
// Silent iframe SSO
|
|
144
|
-
resolveAuthUrl(): string;
|
|
145
|
-
silentSignIn(options?: SilentAuthOptions): Promise<SessionLoginResponse | null>;
|
|
146
|
-
waitForIframeAuth(
|
|
147
|
-
iframe: HTMLIFrameElement,
|
|
148
|
-
timeout: number,
|
|
149
|
-
expectedOrigin: string,
|
|
150
|
-
): Promise<SessionLoginResponse | null>;
|
|
151
|
-
|
|
152
|
-
// Redirect authentication
|
|
153
|
-
signInWithRedirect(options?: RedirectAuthOptions): void;
|
|
154
|
-
signUpWithRedirect(options?: RedirectAuthOptions): void;
|
|
155
|
-
|
|
156
|
-
// Central cross-domain SSO (opaque single-use code exchange)
|
|
157
|
-
exchangeSsoCode(code: string, state?: string): Promise<SessionLoginResponse>;
|
|
158
|
-
generateSsoState(): string;
|
|
159
|
-
|
|
160
122
|
// Express.js middleware
|
|
161
123
|
auth(options?: {
|
|
162
124
|
debug?: boolean;
|