@oxyhq/core 5.4.3 → 6.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 +6 -3
- 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 +47 -43
- package/dist/cjs/mixins/OxyServices.accounts.js +6 -13
- package/dist/cjs/mixins/OxyServices.auth.js +66 -201
- package/dist/cjs/mixins/OxyServices.authorizedApps.js +38 -0
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +119 -0
- package/dist/cjs/mixins/index.js +13 -17
- package/dist/cjs/session/SessionClient.js +44 -2
- package/dist/cjs/session/authStateStore.js +284 -0
- package/dist/cjs/session/createSessionClient.js +8 -2
- package/dist/cjs/session/refresh.js +264 -0
- package/dist/cjs/utils/accountUtils.js +1 -55
- package/dist/cjs/utils/authWebUrl.js +6 -15
- package/dist/cjs/utils/fapiAutoDetect.js +16 -64
- package/dist/cjs/utils/platform.js +19 -0
- package/dist/cjs/utils/ssoBounce.js +15 -340
- package/dist/cjs/utils/validationUtils.js +57 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +6 -3
- 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 +28 -18
- package/dist/esm/mixins/OxyServices.accounts.js +6 -13
- package/dist/esm/mixins/OxyServices.auth.js +66 -201
- package/dist/esm/mixins/OxyServices.authorizedApps.js +35 -0
- package/dist/esm/mixins/OxyServices.deviceBoot.js +116 -0
- package/dist/esm/mixins/index.js +13 -17
- package/dist/esm/session/SessionClient.js +44 -2
- package/dist/esm/session/authStateStore.js +278 -0
- package/dist/esm/session/createSessionClient.js +8 -2
- package/dist/esm/session/refresh.js +257 -0
- package/dist/esm/utils/accountUtils.js +0 -53
- package/dist/esm/utils/authWebUrl.js +6 -14
- package/dist/esm/utils/fapiAutoDetect.js +16 -63
- package/dist/esm/utils/platform.js +18 -0
- package/dist/esm/utils/ssoBounce.js +14 -324
- package/dist/esm/utils/validationUtils.js +56 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +13 -0
- 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 +16 -21
- package/dist/types/mixins/OxyServices.accounts.d.ts +0 -2
- 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.redirect.d.ts → OxyServices.authorizedApps.d.ts} +35 -33
- 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 +6 -9
- package/dist/types/models/interfaces.d.ts +0 -67
- package/dist/types/session/SessionClient.d.ts +38 -1
- package/dist/types/session/authStateStore.d.ts +119 -0
- package/dist/types/session/createSessionClient.d.ts +8 -1
- package/dist/types/session/refresh.d.ts +93 -0
- package/dist/types/utils/accountUtils.d.ts +0 -14
- package/dist/types/utils/authWebUrl.d.ts +6 -12
- package/dist/types/utils/fapiAutoDetect.d.ts +15 -38
- package/dist/types/utils/platform.d.ts +14 -0
- package/dist/types/utils/ssoBounce.d.ts +14 -262
- package/dist/types/utils/validationUtils.d.ts +15 -0
- package/package.json +2 -2
- package/src/HttpService.ts +19 -3
- 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 +78 -64
- package/src/mixins/OxyServices.accounts.ts +6 -13
- package/src/mixins/OxyServices.auth.ts +78 -253
- package/src/mixins/OxyServices.authorizedApps.ts +75 -0
- 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__/authorizedApps.test.ts +63 -0
- package/src/mixins/__tests__/passwordSignIn.test.ts +91 -0
- package/src/mixins/index.ts +16 -22
- package/src/models/interfaces.ts +0 -79
- package/src/session/SessionClient.ts +69 -3
- 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.socketFactory.test.ts +79 -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/createSessionClient.ts +9 -1
- package/src/session/refresh.ts +334 -0
- package/src/utils/__tests__/authWebUrl.test.ts +5 -29
- package/src/utils/__tests__/fapiAutoDetect.test.ts +5 -126
- package/src/utils/__tests__/validationUtils.test.ts +30 -0
- package/src/utils/accountUtils.ts +0 -62
- package/src/utils/authWebUrl.ts +6 -15
- package/src/utils/fapiAutoDetect.ts +16 -60
- package/src/utils/platform.ts +21 -0
- package/src/utils/ssoBounce.ts +14 -371
- 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/ssoEstablish.js +0 -110
- package/dist/cjs/utils/ssoReturn.js +0 -267
- 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/ssoEstablish.js +0 -107
- package/dist/esm/utils/ssoReturn.js +0 -263
- 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.silent.d.ts +0 -132
- package/dist/types/mixins/OxyServices.sso.d.ts +0 -138
- package/dist/types/utils/ssoEstablish.d.ts +0 -85
- package/dist/types/utils/ssoReturn.d.ts +0 -146
- 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__/ssoBounce.test.ts +0 -219
- package/src/utils/__tests__/ssoEstablish.test.ts +0 -204
- package/src/utils/__tests__/ssoReturn.test.ts +0 -251
- package/src/utils/ssoEstablish.ts +0 -174
- package/src/utils/ssoReturn.ts +0 -372
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse the SSO return fragment delivered by the central IdP.
|
|
3
|
-
*
|
|
4
|
-
* After a top-level redirect bounce to `auth.oxy.so/sso` (prompt=none), the
|
|
5
|
-
* central IdP returns the Relying Party to its `redirect_uri` with the result
|
|
6
|
-
* encoded in the URL fragment (the `#…` part). The fragment is used — not a
|
|
7
|
-
* query string — so the opaque single-use code never reaches a server access
|
|
8
|
-
* log, a `Referer` header, or browser history in a recoverable form.
|
|
9
|
-
*
|
|
10
|
-
* Three outcomes are possible:
|
|
11
|
-
* - `#oxy_sso=ok&code=<opaque>&state=<state>` — the IdP had a session; the RP
|
|
12
|
-
* exchanges `code` (via `oxy.exchangeSsoCode`) for the real session. NO
|
|
13
|
-
* token/JWT ever appears in the URL — only the opaque code.
|
|
14
|
-
* - `#oxy_sso=none&state=<state>` — the IdP had no session (prompt=none, user
|
|
15
|
-
* not signed in centrally). The RP shows its own signed-out UI.
|
|
16
|
-
* - `#oxy_sso=error&state=<state>` — the bounce failed. The RP recovers.
|
|
17
|
-
*
|
|
18
|
-
* This parser is pure and defensive: it never throws, and `kind` is strictly
|
|
19
|
-
* one of `'ok' | 'none' | 'error'`. It returns `null` when the fragment is not
|
|
20
|
-
* an oxy_sso fragment at all (i.e. `oxy_sso` is absent or an unrecognised
|
|
21
|
-
* value), so the caller can ignore unrelated fragments without special-casing.
|
|
22
|
-
*/
|
|
23
|
-
import { SSO_CALLBACK_PATH, ssoStateKey, ssoGuardKey, ssoDestKey, ssoNoSessionKey, ssoAttemptedKey, ssoCallbackBootstrapKey, } from './ssoBounce.js';
|
|
24
|
-
const VALID_KINDS = new Set(['ok', 'none', 'error']);
|
|
25
|
-
/**
|
|
26
|
-
* Parse an SSO return fragment.
|
|
27
|
-
*
|
|
28
|
-
* @param hash - The URL fragment, with or without the leading `#`
|
|
29
|
-
* (e.g. `location.hash`). May be `undefined`/empty.
|
|
30
|
-
* @returns The parsed result when `hash` is a recognised oxy_sso fragment,
|
|
31
|
-
* otherwise `null`. Never throws.
|
|
32
|
-
*/
|
|
33
|
-
export function parseSsoReturnFragment(hash) {
|
|
34
|
-
if (typeof hash !== 'string' || hash.length === 0) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
// Strip a single leading '#'. A bare '#' (empty fragment) yields no params.
|
|
38
|
-
const raw = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
39
|
-
if (raw.length === 0) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
let params;
|
|
43
|
-
try {
|
|
44
|
-
params = new URLSearchParams(raw);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
// URLSearchParams does not throw for malformed input in practice, but guard
|
|
48
|
-
// against any environment/polyfill that might so this stays total.
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
const kind = params.get('oxy_sso');
|
|
52
|
-
if (kind === null || !VALID_KINDS.has(kind)) {
|
|
53
|
-
// Not an oxy_sso fragment (absent or unrecognised value) — ignore it.
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
const result = { kind: kind };
|
|
57
|
-
const state = params.get('state');
|
|
58
|
-
if (state !== null && state.length > 0) {
|
|
59
|
-
result.state = state;
|
|
60
|
-
}
|
|
61
|
-
// The opaque code is only meaningful on success; ignore any stray `code` on
|
|
62
|
-
// none/error so callers never attempt an exchange for a non-ok outcome.
|
|
63
|
-
if (result.kind === 'ok') {
|
|
64
|
-
const code = params.get('code');
|
|
65
|
-
if (code !== null && code.length > 0) {
|
|
66
|
-
result.code = code;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Consume an SSO return: the commit-free, security-critical kernel of the
|
|
73
|
-
* cross-domain SSO `sso-return` cold-boot step.
|
|
74
|
-
*
|
|
75
|
-
* This performs the CSRF/fragment/exchange/dest-restore/loop-breaker sequence
|
|
76
|
-
* and RETURNS the exchanged session (or `null`). It deliberately does NOT
|
|
77
|
-
* commit any UI/auth state — each provider commits its own way AROUND this
|
|
78
|
-
* (e.g. `@oxyhq/services` `OxyContext` calls its `handleWebSSOSession`,
|
|
79
|
-
* `@oxyhq/auth` `WebOxyProvider` updates its React state). Hoisting the kernel
|
|
80
|
-
* here keeps the two providers byte-for-byte identical on the parts that matter
|
|
81
|
-
* for security (state validation, fragment stripping order, loop prevention).
|
|
82
|
-
*
|
|
83
|
-
* Security/loop invariants (preserved exactly from both former copies):
|
|
84
|
-
* - The fragment is stripped via `history.replaceState` FIRST — before the
|
|
85
|
-
* exchange — so the opaque code never lingers in the URL, browser history,
|
|
86
|
-
* or a `Referer` header even if a later step throws.
|
|
87
|
-
* - `state` must match (CSRF). A mismatch or a missing code sets the
|
|
88
|
-
* NO_SESSION flag so `sso-bounce` is disabled (no rebounce loop).
|
|
89
|
-
* - `none`/`error` outcomes set BOTH the NO_SESSION flag and the
|
|
90
|
-
* outcome-independent attempted-flag (the load2 half of the loop proof).
|
|
91
|
-
* - A throwing exchange is caught, reported via `onExchangeError`, and
|
|
92
|
-
* treated exactly like "no session" (never loops, never rethrows).
|
|
93
|
-
* - On EVERY consumed outcome (ok, none, error, state-mismatch, no-code,
|
|
94
|
-
* failed-exchange, no-sessionId) — not just ok — if the page landed on
|
|
95
|
-
* {@link SSO_CALLBACK_PATH}, the user is taken to a same-origin TARGET so
|
|
96
|
-
* they are never stranded on the internal callback path (which is an
|
|
97
|
-
* unregistered route in every consumer router → a hard 404). The target is
|
|
98
|
-
* the stored DEST when it parses as same-origin (an attacker-planted
|
|
99
|
-
* cross-origin / protocol-relative dest is rejected), ELSE the app root
|
|
100
|
-
* (`origin + '/'`). The DEST key is removed unconditionally.
|
|
101
|
-
* - For the `ok` outcome the target is applied via a SOFT
|
|
102
|
-
* `history.replaceState` + synthetic `popstate` so the freshly exchanged
|
|
103
|
-
* in-memory session the provider is about to commit is preserved (no
|
|
104
|
-
* reload). `popstate` is dispatched only on the `ok` same-origin restore.
|
|
105
|
-
* - For every NON-`ok` outcome there is no in-memory session to preserve, and
|
|
106
|
-
* the consumer router has ALREADY synchronously rendered its 404 for the
|
|
107
|
-
* unregistered callback route — a soft replaceState+popstate does not
|
|
108
|
-
* reliably make it re-resolve. So these outcomes perform a HARD
|
|
109
|
-
* full-document navigation to the target (`hardRedirect`), which is both
|
|
110
|
-
* safe (nothing to lose) and guaranteed to clear the 404 in every router.
|
|
111
|
-
*
|
|
112
|
-
* Total: this function NEVER throws. Off-web it is a no-op returning `null`.
|
|
113
|
-
*
|
|
114
|
-
* @param oxy - The exchange surface (`oxyServices.exchangeSsoCode`).
|
|
115
|
-
* @param deps - Injectable web seams; see {@link ConsumeSsoReturnDeps}.
|
|
116
|
-
* @returns The exchanged session on success, otherwise `null`.
|
|
117
|
-
*/
|
|
118
|
-
export async function consumeSsoReturn(oxy, deps = {}) {
|
|
119
|
-
const isWeb = deps.isWeb ??
|
|
120
|
-
(() => typeof window !== 'undefined' &&
|
|
121
|
-
typeof window.sessionStorage !== 'undefined');
|
|
122
|
-
if (!isWeb()) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
const storage = deps.storage ?? window.sessionStorage;
|
|
126
|
-
const location = deps.location ?? window.location;
|
|
127
|
-
const history = deps.history ?? window.history;
|
|
128
|
-
const onExchangeError = deps.onExchangeError;
|
|
129
|
-
// Default: emit a synthetic `popstate` so URL-driven routers re-sync after a
|
|
130
|
-
// `history.replaceState` (which does NOT emit `popstate` on its own). Feature-
|
|
131
|
-
// detected end to end so it never throws in any environment.
|
|
132
|
-
const dispatchPopState = deps.dispatchPopState ??
|
|
133
|
-
(() => {
|
|
134
|
-
if (typeof window === 'undefined' || typeof window.dispatchEvent !== 'function') {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (typeof PopStateEvent !== 'undefined') {
|
|
138
|
-
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
139
|
-
}
|
|
140
|
-
else if (typeof Event !== 'undefined') {
|
|
141
|
-
window.dispatchEvent(new Event('popstate'));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
// Default: a hard, full-document navigation used to leave the callback path
|
|
145
|
-
// on non-`ok` outcomes. Feature-detected end to end so it never throws in any
|
|
146
|
-
// environment (SSR / native / a stubbed location without `replace`).
|
|
147
|
-
const hardRedirect = deps.hardRedirect ??
|
|
148
|
-
((url) => {
|
|
149
|
-
if (typeof window !== 'undefined' &&
|
|
150
|
-
window.location &&
|
|
151
|
-
typeof window.location.replace === 'function') {
|
|
152
|
-
window.location.replace(url);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
const ret = parseSsoReturnFragment(location.hash);
|
|
156
|
-
if (!ret) {
|
|
157
|
-
// Not an oxy_sso fragment — nothing to do (do NOT touch any flags).
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
const origin = location.origin;
|
|
161
|
-
const callbackBootstrapKey = ssoCallbackBootstrapKey(origin);
|
|
162
|
-
const wasCallbackBootstrapped = storage.getItem(callbackBootstrapKey) === '1';
|
|
163
|
-
const expectedState = storage.getItem(ssoStateKey(origin));
|
|
164
|
-
const stateOk = !!ret.state && !!expectedState && ret.state === expectedState;
|
|
165
|
-
// Strip the fragment FIRST so the opaque code never lingers in the address
|
|
166
|
-
// bar, history, or a `Referer` — even if a later step throws.
|
|
167
|
-
history.replaceState(null, '', location.pathname + location.search);
|
|
168
|
-
storage.removeItem(ssoStateKey(origin));
|
|
169
|
-
// The in-flight bounce is now resolved — drop its guard so a later cold boot
|
|
170
|
-
// (e.g. after sign-out) can bounce again.
|
|
171
|
-
storage.removeItem(ssoGuardKey(origin));
|
|
172
|
-
const markNoSession = () => {
|
|
173
|
-
storage.setItem(ssoNoSessionKey(origin), '1');
|
|
174
|
-
// A return was consumed, so the probe definitively happened. Set the
|
|
175
|
-
// outcome-independent attempted-flag too so the bounce can never re-fire
|
|
176
|
-
// even if some consumer path skipped setting it pre-bounce.
|
|
177
|
-
storage.setItem(ssoAttemptedKey(origin), '1');
|
|
178
|
-
};
|
|
179
|
-
// Compute the same-origin TARGET to leave the callback path for. Returns the
|
|
180
|
-
// stored DEST when present AND it parses as same-origin (never honour a
|
|
181
|
-
// cross-origin / protocol-relative dest that could have been planted to
|
|
182
|
-
// redirect the user), ELSE the app root (`origin + '/'`) so the user is never
|
|
183
|
-
// stranded on the internal callback path even when no dest was stored. The
|
|
184
|
-
// DEST key is removed unconditionally. Returns the relative path+search+hash
|
|
185
|
-
// (so it can be fed to either `history.replaceState` or a `hardRedirect`),
|
|
186
|
-
// or `null` when the page is not on the callback path (nothing to leave).
|
|
187
|
-
const consumeCallbackTarget = () => {
|
|
188
|
-
storage.removeItem(callbackBootstrapKey);
|
|
189
|
-
if (location.pathname !== SSO_CALLBACK_PATH && !wasCallbackBootstrapped) {
|
|
190
|
-
// Not on the callback path — still drop the dest key (consumed) but there
|
|
191
|
-
// is nothing to navigate away from.
|
|
192
|
-
storage.removeItem(ssoDestKey(origin));
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
const dest = storage.getItem(ssoDestKey(origin));
|
|
196
|
-
storage.removeItem(ssoDestKey(origin));
|
|
197
|
-
if (dest) {
|
|
198
|
-
try {
|
|
199
|
-
const destUrl = new URL(dest, origin);
|
|
200
|
-
if (destUrl.origin === origin) {
|
|
201
|
-
return destUrl.pathname + destUrl.search + destUrl.hash;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
// Malformed stored destination — fall through to the app-root fallback.
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// No dest, a cross-origin/protocol-relative dest, or an unparseable dest:
|
|
209
|
-
// fall back to the app root so the router always leaves the 404.
|
|
210
|
-
return '/';
|
|
211
|
-
};
|
|
212
|
-
// Non-`ok` outcomes: there is no in-memory session to preserve, and the
|
|
213
|
-
// consumer router has already rendered its 404 for the unregistered callback
|
|
214
|
-
// route — a soft replaceState+popstate does not reliably make it re-resolve.
|
|
215
|
-
// Perform a HARD full-document navigation to the target (safe: nothing to
|
|
216
|
-
// lose; guaranteed: every router leaves the 404). Off the callback path this
|
|
217
|
-
// is a no-op (target is null).
|
|
218
|
-
const leaveCallbackHard = () => {
|
|
219
|
-
const target = consumeCallbackTarget();
|
|
220
|
-
if (target !== null) {
|
|
221
|
-
hardRedirect(origin + target);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
if (ret.kind === 'none' || ret.kind === 'error') {
|
|
225
|
-
// The central IdP had no session (or the bounce failed). Record it so we do
|
|
226
|
-
// not bounce again this tab — the definitive loop breaker.
|
|
227
|
-
markNoSession();
|
|
228
|
-
leaveCallbackHard();
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
if (!stateOk || !ret.code) {
|
|
232
|
-
// Forged / replayed / stale fragment, or a malformed ok with no code. Treat
|
|
233
|
-
// exactly like "no session": never exchange, never loop.
|
|
234
|
-
markNoSession();
|
|
235
|
-
leaveCallbackHard();
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
let session;
|
|
239
|
-
try {
|
|
240
|
-
session = await oxy.exchangeSsoCode(ret.code, ret.state);
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
onExchangeError?.(error);
|
|
244
|
-
markNoSession();
|
|
245
|
-
leaveCallbackHard();
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
if (!session?.sessionId) {
|
|
249
|
-
markNoSession();
|
|
250
|
-
leaveCallbackHard();
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
// `ok`: the provider is about to commit the freshly exchanged in-memory
|
|
254
|
-
// session — do NOT hard-redirect (a full navigation would discard it). Use a
|
|
255
|
-
// SOFT `history.replaceState` to the target + a synthetic `popstate` so
|
|
256
|
-
// URL-driven routers re-sync to the restored route without a reload.
|
|
257
|
-
const target = consumeCallbackTarget();
|
|
258
|
-
if (target !== null) {
|
|
259
|
-
history.replaceState(null, '', target);
|
|
260
|
-
dispatchPopState();
|
|
261
|
-
}
|
|
262
|
-
return session;
|
|
263
|
-
}
|
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AuthManager - Centralized Authentication Manager
|
|
3
|
-
*
|
|
4
|
-
* Provides a unified authentication interface for all platforms.
|
|
5
|
-
* Handles token storage, session management, and auth state changes.
|
|
6
|
-
*
|
|
7
|
-
* @module core/AuthManager
|
|
8
|
-
*/
|
|
9
|
-
import type { OxyServices } from './OxyServices';
|
|
10
|
-
import type { SessionLoginResponse, MinimalUserData } from './models/session';
|
|
11
|
-
import type { AuthManagerAccount, RestoreFromCookiesResult, RestoreFromCookiesOptions, SwitchAuthuserResult } from './AuthManagerTypes';
|
|
12
|
-
/**
|
|
13
|
-
* Storage adapter interface for platform-agnostic storage.
|
|
14
|
-
*/
|
|
15
|
-
export interface StorageAdapter {
|
|
16
|
-
getItem: (key: string) => Promise<string | null> | string | null;
|
|
17
|
-
setItem: (key: string, value: string) => Promise<void> | void;
|
|
18
|
-
removeItem: (key: string) => Promise<void> | void;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Auth state change callback type.
|
|
22
|
-
*/
|
|
23
|
-
export type AuthStateChangeCallback = (user: MinimalUserData | null) => void;
|
|
24
|
-
/**
|
|
25
|
-
* Auth method types.
|
|
26
|
-
*/
|
|
27
|
-
export type AuthMethod = 'fedcm' | 'redirect' | 'credentials' | 'identity';
|
|
28
|
-
/**
|
|
29
|
-
* Auth manager configuration.
|
|
30
|
-
*/
|
|
31
|
-
export interface AuthManagerConfig {
|
|
32
|
-
/** Storage adapter (localStorage, AsyncStorage, etc.) */
|
|
33
|
-
storage?: StorageAdapter;
|
|
34
|
-
/** Whether to auto-refresh tokens */
|
|
35
|
-
autoRefresh?: boolean;
|
|
36
|
-
/** Token refresh interval in milliseconds (default: 5 minutes before expiry) */
|
|
37
|
-
refreshBuffer?: number;
|
|
38
|
-
/** Enable cross-tab coordination via BroadcastChannel (default: true in browsers) */
|
|
39
|
-
crossTabSync?: boolean;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* AuthManager - Centralized authentication management.
|
|
43
|
-
*
|
|
44
|
-
* Provides a single point of control for:
|
|
45
|
-
* - Token storage and retrieval
|
|
46
|
-
* - Session management
|
|
47
|
-
* - Auth state change notifications
|
|
48
|
-
* - Multiple auth method support
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* const authManager = new AuthManager(oxyServices);
|
|
53
|
-
*
|
|
54
|
-
* // Listen for auth changes
|
|
55
|
-
* authManager.onAuthStateChange((user) => {
|
|
56
|
-
* console.log('Auth state changed:', user);
|
|
57
|
-
* });
|
|
58
|
-
*
|
|
59
|
-
* // Handle successful auth
|
|
60
|
-
* await authManager.handleAuthSuccess(session);
|
|
61
|
-
*
|
|
62
|
-
* // Sign out
|
|
63
|
-
* await authManager.signOut();
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
export declare class AuthManager {
|
|
67
|
-
private oxyServices;
|
|
68
|
-
private storage;
|
|
69
|
-
private listeners;
|
|
70
|
-
private currentUser;
|
|
71
|
-
private currentAuthMethod;
|
|
72
|
-
private refreshTimer;
|
|
73
|
-
private refreshPromise;
|
|
74
|
-
private config;
|
|
75
|
-
/** Tracks the access token this instance last knew about, for cross-tab adoption. */
|
|
76
|
-
private _lastKnownAccessToken;
|
|
77
|
-
/** BroadcastChannel for coordinating token refreshes across browser tabs. */
|
|
78
|
-
private _broadcastChannel;
|
|
79
|
-
/**
|
|
80
|
-
* Identifier for this AuthManager instance (≈ "this tab"). Random hex
|
|
81
|
-
* generated at construction; advertised in every outgoing broadcast and
|
|
82
|
-
* used as the lookup key in `_knownPeerNonces`.
|
|
83
|
-
*/
|
|
84
|
-
private readonly _tabId;
|
|
85
|
-
/**
|
|
86
|
-
* Per-tab nonce, advertised in every outgoing broadcast. Receivers record
|
|
87
|
-
* the first (tabId, nonce) pair they see from a given peer; subsequent
|
|
88
|
-
* messages from the same tabId MUST carry the same nonce or they're
|
|
89
|
-
* ignored.
|
|
90
|
-
*
|
|
91
|
-
* Threat model: a same-origin XSS payload can post to the channel but can
|
|
92
|
-
* NOT read this instance's private `_broadcastNonce` field (it lives in
|
|
93
|
-
* closure, not on `window`). Forged broadcasts from XSS therefore can't
|
|
94
|
-
* impersonate this tab. A new attacker-controlled tabId trips the
|
|
95
|
-
* "first message from a new peer" branch, which is by definition trusted
|
|
96
|
-
* — so the gate raises the bar but is not a complete defence (a perfect
|
|
97
|
-
* mitigation would require message signing with a server-issued key).
|
|
98
|
-
*/
|
|
99
|
-
private readonly _broadcastNonce;
|
|
100
|
-
/**
|
|
101
|
-
* Bounded LRU of `(tabId → nonce)` pairs seen on inbound broadcasts. First
|
|
102
|
-
* sighting of a new tabId records its nonce; later messages from that
|
|
103
|
-
* tabId are rejected if the nonce doesn't match.
|
|
104
|
-
*/
|
|
105
|
-
private readonly _knownPeerNonces;
|
|
106
|
-
private static readonly _MAX_KNOWN_PEERS;
|
|
107
|
-
/**
|
|
108
|
-
* In-flight `switchAuthuser` promise. Deduplicates concurrent calls so two
|
|
109
|
-
* near-simultaneous switches don't both fire refresh requests and rotate
|
|
110
|
-
* the slot twice. Mirrors the `refreshPromise` pattern used by
|
|
111
|
-
* `refreshToken`.
|
|
112
|
-
*/
|
|
113
|
-
private _switchPromise;
|
|
114
|
-
/**
|
|
115
|
-
* Last `restoreFromCookies()` completion timestamp, keyed by the
|
|
116
|
-
* AuthManager's active authuser at the time of completion. Used to gate
|
|
117
|
-
* cross-tab cascade: a flurry of BroadcastChannel events from sibling
|
|
118
|
-
* tabs can otherwise trigger N back-to-back snapshots and rotate every
|
|
119
|
-
* slot's access token N times.
|
|
120
|
-
*/
|
|
121
|
-
private readonly _lastRestoreAt;
|
|
122
|
-
private static readonly _RESTORE_DEBOUNCE_MS;
|
|
123
|
-
/**
|
|
124
|
-
* In-memory registry of every device-local account the AuthManager knows
|
|
125
|
-
* about, keyed by `authuser` slot index. Populated by:
|
|
126
|
-
* - `restoreFromCookies()` (cold boot)
|
|
127
|
-
* - `switchAuthuser()` (per-account rotation)
|
|
128
|
-
* - `handleAuthSuccess()` (fresh login when the server response carries
|
|
129
|
-
* an `authuser` field)
|
|
130
|
-
* Access tokens live ONLY here in the cookie path — they are never
|
|
131
|
-
* persisted to localStorage.
|
|
132
|
-
*/
|
|
133
|
-
private accounts;
|
|
134
|
-
/**
|
|
135
|
-
* Currently-active `authuser` slot in the cookie path. `null` means either
|
|
136
|
-
* the cookie path hasn't been initialised yet, or no slots are signed in.
|
|
137
|
-
*/
|
|
138
|
-
private activeAuthuser;
|
|
139
|
-
constructor(oxyServices: OxyServices, config?: AuthManagerConfig);
|
|
140
|
-
/**
|
|
141
|
-
* Initialize BroadcastChannel for cross-tab token refresh coordination.
|
|
142
|
-
* Only called in browser environments where BroadcastChannel is available.
|
|
143
|
-
*/
|
|
144
|
-
private _initBroadcastChannel;
|
|
145
|
-
/**
|
|
146
|
-
* Handle messages from other tabs about token refresh activity.
|
|
147
|
-
*/
|
|
148
|
-
private _handleCrossTabMessage;
|
|
149
|
-
/**
|
|
150
|
-
* Broadcast a message to other tabs. Always stamps this tab's `tabId` and
|
|
151
|
-
* `nonce` onto the message so receivers can run the cross-tab nonce gate.
|
|
152
|
-
*/
|
|
153
|
-
private _broadcast;
|
|
154
|
-
/**
|
|
155
|
-
* Generate `bytes` bytes of cryptographic randomness encoded as lowercase
|
|
156
|
-
* hex. Prefers Web Crypto's `getRandomValues` when available (browser /
|
|
157
|
-
* modern Node); falls back to `Math.random` ONLY in environments without
|
|
158
|
-
* Web Crypto (the resulting nonce is still unguessable to a same-origin
|
|
159
|
-
* XSS payload — the goal is unforgeability across tabs, not cryptographic
|
|
160
|
-
* secrecy across the network).
|
|
161
|
-
*/
|
|
162
|
-
private static _randomHex;
|
|
163
|
-
/**
|
|
164
|
-
* Validate an inbound broadcast against the cross-tab nonce gate.
|
|
165
|
-
*
|
|
166
|
-
* Returns `true` when the message should be honoured, `false` when it
|
|
167
|
-
* MUST be ignored:
|
|
168
|
-
* - Message is missing `tabId` or `nonce` → ignore (forged or
|
|
169
|
-
* mismatched-version sibling tab).
|
|
170
|
-
* - First sighting of `tabId` → record the nonce and honour the message
|
|
171
|
-
* (trust-on-first-use, the best we can do without a shared secret).
|
|
172
|
-
* - Subsequent message from the same `tabId` with the SAME nonce →
|
|
173
|
-
* honour.
|
|
174
|
-
* - Subsequent message from the same `tabId` with a DIFFERENT nonce →
|
|
175
|
-
* ignore (the canonical "forged broadcast" case — a same-origin XSS
|
|
176
|
-
* payload can't read the real tab's `_broadcastNonce`).
|
|
177
|
-
*
|
|
178
|
-
* Echoes of this tab's own broadcasts (same `tabId`) are also dropped so
|
|
179
|
-
* we don't react to our own messages.
|
|
180
|
-
*/
|
|
181
|
-
private _acceptBroadcast;
|
|
182
|
-
/**
|
|
183
|
-
* Get default storage based on environment.
|
|
184
|
-
*/
|
|
185
|
-
private getDefaultStorage;
|
|
186
|
-
/**
|
|
187
|
-
* Subscribe to auth state changes.
|
|
188
|
-
*
|
|
189
|
-
* @param callback - Function called when auth state changes
|
|
190
|
-
* @returns Unsubscribe function
|
|
191
|
-
*/
|
|
192
|
-
onAuthStateChange(callback: AuthStateChangeCallback): () => void;
|
|
193
|
-
/**
|
|
194
|
-
* Notify all listeners of auth state change.
|
|
195
|
-
*/
|
|
196
|
-
private notifyListeners;
|
|
197
|
-
/**
|
|
198
|
-
* Handle successful authentication.
|
|
199
|
-
*
|
|
200
|
-
* @param session - Session response from auth
|
|
201
|
-
* @param method - Auth method used
|
|
202
|
-
*/
|
|
203
|
-
handleAuthSuccess(session: SessionLoginResponse, method?: AuthMethod): Promise<void>;
|
|
204
|
-
/**
|
|
205
|
-
* Refresh the access token. Deduplicates concurrent calls so only one
|
|
206
|
-
* refresh request is in-flight at a time. The only refresh authority is the
|
|
207
|
-
* active httpOnly refresh-cookie slot; this method never reads access tokens
|
|
208
|
-
* from storage.
|
|
209
|
-
*/
|
|
210
|
-
refreshToken(): Promise<boolean>;
|
|
211
|
-
private _doRefreshToken;
|
|
212
|
-
/**
|
|
213
|
-
* Sign out and clear all auth data.
|
|
214
|
-
*/
|
|
215
|
-
signOut(): Promise<void>;
|
|
216
|
-
/**
|
|
217
|
-
* Clear local cookie-path state. The only persisted AuthManager value is the
|
|
218
|
-
* active numeric slot; tokens and user objects are intentionally memory-only.
|
|
219
|
-
*/
|
|
220
|
-
private clearSession;
|
|
221
|
-
/**
|
|
222
|
-
* Get current user.
|
|
223
|
-
*/
|
|
224
|
-
getCurrentUser(): MinimalUserData | null;
|
|
225
|
-
/**
|
|
226
|
-
* Check if user is authenticated.
|
|
227
|
-
*/
|
|
228
|
-
isAuthenticated(): boolean;
|
|
229
|
-
/**
|
|
230
|
-
* Get a valid access token, refreshing automatically if expired or expiring
|
|
231
|
-
* soon. The token is read from memory only.
|
|
232
|
-
*/
|
|
233
|
-
getAccessToken(): Promise<string | null>;
|
|
234
|
-
/**
|
|
235
|
-
* Get the auth method used for current session.
|
|
236
|
-
*/
|
|
237
|
-
getAuthMethod(): Promise<AuthMethod | null>;
|
|
238
|
-
/**
|
|
239
|
-
* Initialize auth state on app startup.
|
|
240
|
-
*
|
|
241
|
-
* Only the cookie path is authoritative. `restoreFromCookies()` refreshes
|
|
242
|
-
* the httpOnly `oxy_rt_${authuser}` slots through `/auth/refresh-all`,
|
|
243
|
-
* plants the active access token in memory, and returns the active user.
|
|
244
|
-
* No access token, refresh token, or session JSON is read from localStorage.
|
|
245
|
-
*
|
|
246
|
-
* Returns the active user on success, or `null` when no cookie-backed
|
|
247
|
-
* account was restored.
|
|
248
|
-
*/
|
|
249
|
-
initialize(options?: RestoreFromCookiesOptions): Promise<MinimalUserData | null>;
|
|
250
|
-
/**
|
|
251
|
-
* Read the persisted active `authuser` slot index. Returns `null` when
|
|
252
|
-
* none is persisted, the value is corrupt, or the storage adapter has no
|
|
253
|
-
* record. Storage failures are non-fatal: the cookie path falls back to
|
|
254
|
-
* "lowest authuser" deterministic selection.
|
|
255
|
-
*/
|
|
256
|
-
private readActiveAuthuser;
|
|
257
|
-
/**
|
|
258
|
-
* Persist the active `authuser` slot index. No-ops on storage failure
|
|
259
|
-
* (e.g. Safari private mode, native SecureStore unavailable) — this is
|
|
260
|
-
* best-effort UX persistence, not authoritative state.
|
|
261
|
-
*/
|
|
262
|
-
private writeActiveAuthuser;
|
|
263
|
-
/**
|
|
264
|
-
* Clear the persisted active `authuser` so the next cold boot starts from
|
|
265
|
-
* a clean slate (used on full sign-out).
|
|
266
|
-
*/
|
|
267
|
-
private clearActiveAuthuser;
|
|
268
|
-
/**
|
|
269
|
-
* Build a `MinimalUserData` from a `RefreshAllAccount`. Returns `null` when
|
|
270
|
-
* the wire entry has no user shape; the AuthManager's caller is expected to
|
|
271
|
-
* hydrate via `/users/me` in that case.
|
|
272
|
-
*/
|
|
273
|
-
private static toMinimalUser;
|
|
274
|
-
/**
|
|
275
|
-
* Hydrate the user shape for a slot whose AuthManagerAccount currently has
|
|
276
|
-
* `user: null` (for example, a switch onto a previously unknown slot). Calls
|
|
277
|
-
* `/users/me` with the slot's freshly-planted access
|
|
278
|
-
* token already on the HTTP client; merges the result back into the
|
|
279
|
-
* registry entry. Network failures are non-fatal — the slot remains with
|
|
280
|
-
* `user: null` and the UI is expected to render the public-key fallback
|
|
281
|
-
* handle until a later restore picks the real user shape up.
|
|
282
|
-
*/
|
|
283
|
-
private _hydrateUnknownUser;
|
|
284
|
-
/**
|
|
285
|
-
* Snapshot of the registered cookie-path accounts, sorted by `authuser`
|
|
286
|
-
* ascending (canonical order). Mutating the returned array does not
|
|
287
|
-
* affect AuthManager state.
|
|
288
|
-
*/
|
|
289
|
-
getAccounts(): AuthManagerAccount[];
|
|
290
|
-
/**
|
|
291
|
-
* The slot index that is currently active in the cookie path, or `null`
|
|
292
|
-
* if the cookie path hasn't been initialised or no slots are signed in.
|
|
293
|
-
*/
|
|
294
|
-
getActiveAuthuser(): number | null;
|
|
295
|
-
/**
|
|
296
|
-
* Convenience: the AuthManagerAccount currently flagged active.
|
|
297
|
-
*/
|
|
298
|
-
getActiveAccount(): AuthManagerAccount | null;
|
|
299
|
-
/**
|
|
300
|
-
* Restore every device-local account from the httpOnly refresh cookies.
|
|
301
|
-
*
|
|
302
|
-
* Calls `oxyServices.refreshAllSessions()` (`POST /auth/refresh-all` with
|
|
303
|
-
* `credentials: 'include'`). The server rotates every presented
|
|
304
|
-
* `oxy_rt_${authuser}` cookie in parallel and returns one entry per
|
|
305
|
-
* valid slot.
|
|
306
|
-
*
|
|
307
|
-
* Plants the active account's access token on the shared HTTP client;
|
|
308
|
-
* sibling slots' tokens stay in the in-memory registry so a later
|
|
309
|
-
* `switchAuthuser()` can hot-swap them without a network round-trip.
|
|
310
|
-
*
|
|
311
|
-
* The persisted `oxy_active_authuser` slot wins when it matches a
|
|
312
|
-
* returned account; otherwise the lowest returned `authuser` is chosen
|
|
313
|
-
* deterministically.
|
|
314
|
-
*
|
|
315
|
-
* Returns `{ accounts: [], activeAuthuser: null }` on any failure or
|
|
316
|
-
* empty snapshot — callers treat that as "no signed-in accounts" and
|
|
317
|
-
* proceed unauthenticated. State is NOT cleared on failure; existing
|
|
318
|
-
* accounts (if any) remain intact.
|
|
319
|
-
*/
|
|
320
|
-
restoreFromCookies(options?: RestoreFromCookiesOptions): Promise<RestoreFromCookiesResult>;
|
|
321
|
-
/**
|
|
322
|
-
* Switch the active account to a different device-local slot.
|
|
323
|
-
*
|
|
324
|
-
* Calls `oxyServices.refreshTokenViaCookie({ authuser })` to mint a fresh
|
|
325
|
-
* access token from the slot's httpOnly cookie, updates the in-memory
|
|
326
|
-
* registry entry, plants the token on the HTTP client, persists the new
|
|
327
|
-
* active slot, and broadcasts cross-tab.
|
|
328
|
-
*
|
|
329
|
-
* Throws when the slot's refresh cookie is missing / expired / reused
|
|
330
|
-
* (the SDK returns `null` from `refreshTokenViaCookie` in that case, and
|
|
331
|
-
* we surface it as an `Error` so callers can clean up the slot from
|
|
332
|
-
* their UI).
|
|
333
|
-
*/
|
|
334
|
-
switchAuthuser(authuser: number): Promise<SwitchAuthuserResult>;
|
|
335
|
-
private _doSwitchAuthuser;
|
|
336
|
-
/**
|
|
337
|
-
* Sign out a single device-local slot.
|
|
338
|
-
*
|
|
339
|
-
* Calls `oxyServices.logoutSessionByAuthuser(authuser)`: server-side
|
|
340
|
-
* revokes the slot's refresh-token family and clears the
|
|
341
|
-
* `oxy_rt_${authuser}` cookie via `Set-Cookie`. The slot is removed from
|
|
342
|
-
* the in-memory registry. If the slot was active, the next lowest
|
|
343
|
-
* remaining authuser becomes active (or `null` when none remain).
|
|
344
|
-
*/
|
|
345
|
-
signOutAuthuser(authuser: number): Promise<void>;
|
|
346
|
-
/**
|
|
347
|
-
* Sign out EVERY device-local account on this device.
|
|
348
|
-
*
|
|
349
|
-
* Calls `oxyServices.logoutAllSessionsViaCookie()`: server-side revokes
|
|
350
|
-
* every presented family and `Set-Cookie`s an immediate expiry for every
|
|
351
|
-
* recognised `oxy_rt_${n}` slot. The in-memory registry is wiped, the active
|
|
352
|
-
* slot is cleared, and the persisted `oxy_active_authuser` is removed so the
|
|
353
|
-
* next cold boot starts fresh.
|
|
354
|
-
*/
|
|
355
|
-
signOutAllViaCookies(): Promise<void>;
|
|
356
|
-
/**
|
|
357
|
-
* Schedule an auto-refresh for the cookie path on the active slot. The
|
|
358
|
-
* AuthManager has exactly one active slot at a time, so one timer suffices.
|
|
359
|
-
*/
|
|
360
|
-
private setupCookieRefresh;
|
|
361
|
-
/**
|
|
362
|
-
* Decode the session id from an unverified JWT access token. Decode-only
|
|
363
|
-
* (no signature verification) — the server already verified the
|
|
364
|
-
* signature when minting the token. Returns `null` on malformed input.
|
|
365
|
-
*/
|
|
366
|
-
private static decodeSessionIdFromAccessToken;
|
|
367
|
-
private static decodeAuthuserFromAccessToken;
|
|
368
|
-
/**
|
|
369
|
-
* Destroy the auth manager and clean up resources.
|
|
370
|
-
*/
|
|
371
|
-
destroy(): void;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Create an AuthManager instance.
|
|
375
|
-
*
|
|
376
|
-
* @param oxyServices - OxyServices instance
|
|
377
|
-
* @param config - Optional configuration
|
|
378
|
-
* @returns AuthManager instance
|
|
379
|
-
*/
|
|
380
|
-
export declare function createAuthManager(oxyServices: OxyServices, config?: AuthManagerConfig): AuthManager;
|