@oxy.so/services 12.13.0 → 12.14.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/lib/commonjs/ui/boot/runProviderColdBoot.js +188 -88
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +116 -3
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/session/cachedBoot.js +95 -0
- package/lib/commonjs/ui/session/cachedBoot.js.map +1 -0
- package/lib/module/ui/boot/runProviderColdBoot.js +188 -88
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +117 -4
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/session/cachedBoot.js +92 -0
- package/lib/module/ui/session/cachedBoot.js.map +1 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +24 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/cachedBoot.d.ts +48 -0
- package/lib/typescript/commonjs/ui/session/cachedBoot.d.ts.map +1 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +24 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/cachedBoot.d.ts +48 -0
- package/lib/typescript/module/ui/session/cachedBoot.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/ui/boot/__tests__/cachedColdBoot.test.ts +187 -0
- package/src/ui/boot/runProviderColdBoot.ts +229 -95
- package/src/ui/context/OxyContext.tsx +146 -3
- package/src/ui/session/__tests__/cachedBoot.test.ts +32 -0
- package/src/ui/session/cachedBoot.ts +139 -0
|
@@ -88,9 +88,177 @@ async function detectOfflineHint() {
|
|
|
88
88
|
return false;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
/** Starts the provider's fresh classification; a rejection is observed, not swallowed. */
|
|
92
|
+
function startClassification(opts) {
|
|
93
|
+
const classification = (async () => opts.resolveSessionLane?.() ?? (0, _applicationSessionLane.resolveApplicationSessionLane)(opts.oxyServices, opts.clientId))();
|
|
94
|
+
// Observed so a rejection that lands before it is awaited is not reported as
|
|
95
|
+
// unhandled; every caller still awaits it and sees the rejection.
|
|
96
|
+
classification.catch(() => undefined);
|
|
97
|
+
return classification;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** What the device lanes made of this holder: a session, a verdict against it, or no answer. */
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The device lanes of the boot (`runSessionColdBoot`), for a first boot or for
|
|
104
|
+
* the background check of a painted one.
|
|
105
|
+
*
|
|
106
|
+
* A first boot (`painted: false`) is on the critical path: its committed
|
|
107
|
+
* session resolves auth, bounded by {@link SESSION_HANDOFF_DEADLINE_MS}. A
|
|
108
|
+
* painted boot already resolved auth from the cache, so nothing waits on the
|
|
109
|
+
* handoff and the pure-local warm token is skipped: the check needs the mint's
|
|
110
|
+
* verdict, which a still-valid token cannot give.
|
|
111
|
+
*/
|
|
112
|
+
async function restoreDeviceSession(opts, painted) {
|
|
113
|
+
const {
|
|
114
|
+
oxyServices,
|
|
115
|
+
authStore,
|
|
116
|
+
sessionMode = 'account',
|
|
117
|
+
identity,
|
|
118
|
+
sessionClient,
|
|
119
|
+
syncDeviceCredentialToHost,
|
|
120
|
+
commitSession,
|
|
121
|
+
markAuthResolved
|
|
122
|
+
} = opts;
|
|
123
|
+
await opts.beforeDeviceBoot?.();
|
|
124
|
+
|
|
125
|
+
// Best-effort connectivity probe up front: an EXPLICIT offline verdict skips
|
|
126
|
+
// the two doomed network steps so routing settles immediately instead of
|
|
127
|
+
// burning the overall deadline on a mint that cannot succeed. Any ambiguity
|
|
128
|
+
// resolves to "online" — the network steps still run.
|
|
129
|
+
const offline = await detectOfflineHint();
|
|
130
|
+
let outcome = 'unreachable';
|
|
131
|
+
let holderRejected = false;
|
|
132
|
+
await (0, _session.runSessionColdBoot)({
|
|
133
|
+
oxy: oxyServices,
|
|
134
|
+
store: authStore,
|
|
135
|
+
platform: {
|
|
136
|
+
isWeb: (0, _isWebBrowser.isWebBrowser)(),
|
|
137
|
+
isNative: !(0, _isWebBrowser.isWebBrowser)()
|
|
138
|
+
},
|
|
139
|
+
sessionMode,
|
|
140
|
+
identity,
|
|
141
|
+
// The device-wide shared credential slot, enabling the `shared-device-adopt`
|
|
142
|
+
// lane: a newly installed official app joins this device's existing session
|
|
143
|
+
// instead of falling back to signing a challenge with the Commons identity
|
|
144
|
+
// key. `null` on web, where each origin is its own device by design; the
|
|
145
|
+
// core lane is also skipped outright in `sessionMode: 'identity'`.
|
|
146
|
+
sharedDeviceCredential: (0, _sharedDeviceCredentialStore.createPlatformSharedDeviceCredentialStore)() ?? undefined,
|
|
147
|
+
overallDeadlineMs: COLD_BOOT_OVERALL_DEADLINE_MS,
|
|
148
|
+
isOffline: () => offline,
|
|
149
|
+
requireServerVerdict: painted,
|
|
150
|
+
onHolderRejected: () => {
|
|
151
|
+
holderRejected = true;
|
|
152
|
+
},
|
|
153
|
+
onStepDeadline: stepId => {
|
|
154
|
+
_core.logger.warn(`Cold-boot step "${stepId}" exceeded the ${COLD_BOOT_OVERALL_DEADLINE_MS}ms overall deadline — abandoned; routing proceeds signed-out`, {
|
|
155
|
+
component: 'runProviderColdBoot',
|
|
156
|
+
method: 'onStepDeadline'
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
onSession: async session => {
|
|
160
|
+
outcome = 'session';
|
|
161
|
+
// Mint already persisted `{deviceId, deviceSecret}` to the store; sync the
|
|
162
|
+
// in-memory SessionClient host so sockets + tab-focus re-mint can use it.
|
|
163
|
+
await syncDeviceCredentialToHost();
|
|
164
|
+
const handoff = commitSession({
|
|
165
|
+
sessionId: session.sessionId,
|
|
166
|
+
accessToken: session.accessToken,
|
|
167
|
+
userId: session.userId,
|
|
168
|
+
deviceState: session.state
|
|
169
|
+
}, {
|
|
170
|
+
activate: false
|
|
171
|
+
});
|
|
172
|
+
if (painted) {
|
|
173
|
+
// The account is already on screen; the commit refreshes its profile.
|
|
174
|
+
await handoff;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
let handoffDeadlineId;
|
|
178
|
+
await Promise.race([handoff, new Promise(resolve => {
|
|
179
|
+
handoffDeadlineId = setTimeout(resolve, SESSION_HANDOFF_DEADLINE_MS);
|
|
180
|
+
})]).finally(() => {
|
|
181
|
+
if (handoffDeadlineId !== undefined) {
|
|
182
|
+
clearTimeout(handoffDeadlineId);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
markAuthResolved();
|
|
186
|
+
},
|
|
187
|
+
onSignedOut: async () => {
|
|
188
|
+
outcome = holderRejected ? 'rejected' : 'unreachable';
|
|
189
|
+
await syncDeviceCredentialToHost();
|
|
190
|
+
// A painted session is the caller's to end or keep (see verifyCachedSession).
|
|
191
|
+
if (painted) return;
|
|
192
|
+
const cred = await (0, _deviceCredential.loadPersistedDeviceCredential)(authStore);
|
|
193
|
+
if (cred) {
|
|
194
|
+
try {
|
|
195
|
+
await sessionClient.start();
|
|
196
|
+
} catch (socketError) {
|
|
197
|
+
if (__DEV__) {
|
|
198
|
+
_core.logger.debug('Device socket start failed (non-fatal)', {
|
|
199
|
+
component: 'runProviderColdBoot'
|
|
200
|
+
}, socketError);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
markAuthResolved();
|
|
205
|
+
},
|
|
206
|
+
onStepError: (id, error) => {
|
|
207
|
+
if (__DEV__) {
|
|
208
|
+
_core.logger.debug(`Cold-boot step "${id}" errored (non-fatal, falling through)`, {
|
|
209
|
+
component: 'runProviderColdBoot'
|
|
210
|
+
}, error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
await opts.afterDeviceBoot?.();
|
|
215
|
+
return outcome;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* The background check of a painted session: the registry's classification and
|
|
220
|
+
* a mint with this holder's own credential (whose commit refreshes the profile).
|
|
221
|
+
* Each is asked until the server answers it. A verdict against the session
|
|
222
|
+
* signs out at that moment; no answer keeps the cached session on screen and
|
|
223
|
+
* hands the caller a retry.
|
|
224
|
+
*/
|
|
225
|
+
async function verifyCachedSession(opts, verified, classification) {
|
|
226
|
+
if (!verified.lane) {
|
|
227
|
+
let lane = null;
|
|
228
|
+
try {
|
|
229
|
+
lane = await classification;
|
|
230
|
+
} catch {
|
|
231
|
+
// The registry could not be asked (offline, timeout, 5xx). That is no
|
|
232
|
+
// verdict on this app: the cached `device` classification stands until it
|
|
233
|
+
// answers.
|
|
234
|
+
}
|
|
235
|
+
if (lane === 'oauth') {
|
|
236
|
+
await opts.onCachedSessionRejected?.();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
verified.lane = lane === 'device';
|
|
240
|
+
}
|
|
241
|
+
if (!verified.session) {
|
|
242
|
+
const outcome = await restoreDeviceSession(opts, true);
|
|
243
|
+
if (outcome === 'rejected') {
|
|
244
|
+
await opts.onCachedSessionRejected?.();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
verified.session = outcome === 'session';
|
|
248
|
+
}
|
|
249
|
+
if (!verified.lane || !verified.session) {
|
|
250
|
+
opts.onCachedSessionUnconfirmed?.(() => verifyCachedSession(opts, verified, verified.lane ? Promise.resolve('device') : startClassification(opts)));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
91
254
|
/**
|
|
92
255
|
* Device-first cold boot for `@oxy.so/services` providers.
|
|
93
256
|
*
|
|
257
|
+
* A returning session saved on this device (account mode, the app last
|
|
258
|
+
* classified `device`, its profile cached) paints first, with no network, and
|
|
259
|
+
* is then checked in the background — see `paintCachedSession`. Otherwise, a
|
|
260
|
+
* first boot:
|
|
261
|
+
*
|
|
94
262
|
* Ordered pipeline:
|
|
95
263
|
* 1. Complete an OAuth authorization-code return already on the URL (web)
|
|
96
264
|
* 2. `runSessionColdBoot` — device-secret mint (+ the native Commons-proof lane,
|
|
@@ -113,9 +281,6 @@ async function runProviderColdBoot(opts) {
|
|
|
113
281
|
clientId,
|
|
114
282
|
authRedirectUri,
|
|
115
283
|
sessionMode = 'account',
|
|
116
|
-
identity,
|
|
117
|
-
sessionClient,
|
|
118
|
-
syncDeviceCredentialToHost,
|
|
119
284
|
commitSession,
|
|
120
285
|
markAuthResolved,
|
|
121
286
|
setTokenReady
|
|
@@ -124,13 +289,24 @@ async function runProviderColdBoot(opts) {
|
|
|
124
289
|
setTokenReady(false);
|
|
125
290
|
try {
|
|
126
291
|
// Classification precedes every USE of a local secret, the shared credential
|
|
127
|
-
// and the identity key. Unavailable registry metadata fails closed
|
|
128
|
-
// offline
|
|
129
|
-
// overlaps the local work below.
|
|
130
|
-
const classification = (
|
|
131
|
-
|
|
132
|
-
//
|
|
133
|
-
|
|
292
|
+
// and the identity key. Unavailable registry metadata fails closed on a first
|
|
293
|
+
// boot, including offline. It starts now, before anything else, so its round
|
|
294
|
+
// trip overlaps the local work below.
|
|
295
|
+
const classification = startClassification(opts);
|
|
296
|
+
|
|
297
|
+
// A returning session saved on this device paints at once from the cache:
|
|
298
|
+
// the account, its cached content, auth resolved, no network on the way.
|
|
299
|
+
// The classification and the session are then checked in the background,
|
|
300
|
+
// and a verdict against either signs out at that moment. Identity-bound
|
|
301
|
+
// clients and OAuth returns keep the path below.
|
|
302
|
+
const freshLaunch = !identityBound && !hasOAuthReturnOnUrl();
|
|
303
|
+
if (freshLaunch && (await opts.paintCachedSession?.())) {
|
|
304
|
+
await verifyCachedSession(opts, {
|
|
305
|
+
lane: false,
|
|
306
|
+
session: false
|
|
307
|
+
}, classification);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
134
310
|
|
|
135
311
|
// The verdict decides HOW a session is restored, never WHETHER there is one.
|
|
136
312
|
// A web origin with nothing to restore and no OAuth return on the URL ends
|
|
@@ -138,7 +314,7 @@ async function runProviderColdBoot(opts) {
|
|
|
138
314
|
// for an answer that cannot change the outcome. The classification still
|
|
139
315
|
// completes below and still gates every later sign-in
|
|
140
316
|
// (`ensureDeviceSessionLane` shares this same in-flight lookup).
|
|
141
|
-
if (
|
|
317
|
+
if (freshLaunch && (0, _isWebBrowser.isWebBrowser)()) {
|
|
142
318
|
if (!(await hasRestorableState(authStore))) {
|
|
143
319
|
markAuthResolved();
|
|
144
320
|
const lane = await classification;
|
|
@@ -174,83 +350,7 @@ async function runProviderColdBoot(opts) {
|
|
|
174
350
|
setTokenReady(true);
|
|
175
351
|
return;
|
|
176
352
|
}
|
|
177
|
-
await opts
|
|
178
|
-
|
|
179
|
-
// Best-effort connectivity probe up front: an EXPLICIT offline verdict skips
|
|
180
|
-
// the two doomed network steps so routing settles immediately instead of
|
|
181
|
-
// burning the overall deadline on a mint that cannot succeed. Any ambiguity
|
|
182
|
-
// resolves to "online" — the network steps still run.
|
|
183
|
-
const offline = await detectOfflineHint();
|
|
184
|
-
await (0, _session.runSessionColdBoot)({
|
|
185
|
-
oxy: oxyServices,
|
|
186
|
-
store: authStore,
|
|
187
|
-
platform: {
|
|
188
|
-
isWeb: (0, _isWebBrowser.isWebBrowser)(),
|
|
189
|
-
isNative: !(0, _isWebBrowser.isWebBrowser)()
|
|
190
|
-
},
|
|
191
|
-
sessionMode,
|
|
192
|
-
identity,
|
|
193
|
-
// The device-wide shared credential slot, enabling the `shared-device-adopt`
|
|
194
|
-
// lane: a newly installed official app joins this device's existing session
|
|
195
|
-
// instead of falling back to signing a challenge with the Commons identity
|
|
196
|
-
// key. `null` on web, where each origin is its own device by design; the
|
|
197
|
-
// core lane is also skipped outright in `sessionMode: 'identity'`.
|
|
198
|
-
sharedDeviceCredential: (0, _sharedDeviceCredentialStore.createPlatformSharedDeviceCredentialStore)() ?? undefined,
|
|
199
|
-
overallDeadlineMs: COLD_BOOT_OVERALL_DEADLINE_MS,
|
|
200
|
-
isOffline: () => offline,
|
|
201
|
-
onStepDeadline: stepId => {
|
|
202
|
-
_core.logger.warn(`Cold-boot step "${stepId}" exceeded the ${COLD_BOOT_OVERALL_DEADLINE_MS}ms overall deadline — abandoned; routing proceeds signed-out`, {
|
|
203
|
-
component: 'runProviderColdBoot',
|
|
204
|
-
method: 'onStepDeadline'
|
|
205
|
-
});
|
|
206
|
-
},
|
|
207
|
-
onSession: async session => {
|
|
208
|
-
// Mint already persisted `{deviceId, deviceSecret}` to the store; sync the
|
|
209
|
-
// in-memory SessionClient host so sockets + tab-focus re-mint can use it.
|
|
210
|
-
await syncDeviceCredentialToHost();
|
|
211
|
-
const handoff = commitSession({
|
|
212
|
-
sessionId: session.sessionId,
|
|
213
|
-
accessToken: session.accessToken,
|
|
214
|
-
userId: session.userId,
|
|
215
|
-
deviceState: session.state
|
|
216
|
-
}, {
|
|
217
|
-
activate: false
|
|
218
|
-
});
|
|
219
|
-
let handoffDeadlineId;
|
|
220
|
-
await Promise.race([handoff, new Promise(resolve => {
|
|
221
|
-
handoffDeadlineId = setTimeout(resolve, SESSION_HANDOFF_DEADLINE_MS);
|
|
222
|
-
})]).finally(() => {
|
|
223
|
-
if (handoffDeadlineId !== undefined) {
|
|
224
|
-
clearTimeout(handoffDeadlineId);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
markAuthResolved();
|
|
228
|
-
},
|
|
229
|
-
onSignedOut: async () => {
|
|
230
|
-
await syncDeviceCredentialToHost();
|
|
231
|
-
const cred = await (0, _deviceCredential.loadPersistedDeviceCredential)(authStore);
|
|
232
|
-
if (cred) {
|
|
233
|
-
try {
|
|
234
|
-
await sessionClient.start();
|
|
235
|
-
} catch (socketError) {
|
|
236
|
-
if (__DEV__) {
|
|
237
|
-
_core.logger.debug('Device socket start failed (non-fatal)', {
|
|
238
|
-
component: 'runProviderColdBoot'
|
|
239
|
-
}, socketError);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
markAuthResolved();
|
|
244
|
-
},
|
|
245
|
-
onStepError: (id, error) => {
|
|
246
|
-
if (__DEV__) {
|
|
247
|
-
_core.logger.debug(`Cold-boot step "${id}" errored (non-fatal, falling through)`, {
|
|
248
|
-
component: 'runProviderColdBoot'
|
|
249
|
-
}, error);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
await opts.afterDeviceBoot?.();
|
|
353
|
+
await restoreDeviceSession(opts, false);
|
|
254
354
|
} catch (error) {
|
|
255
355
|
if (__DEV__) {
|
|
256
356
|
_core.logger.error('Cold boot error', error instanceof Error ? error : new Error(String(error)), {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_applicationSessionLane","require","_core","_session","_deviceCredential","_sharedDeviceCredentialStore","_oauthReturn","_isWebBrowser","_netConnectivity","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","hasOAuthReturnOnUrl","search","globalThis","location","params","URLSearchParams","hasRestorableState","store","persisted","load","Boolean","accessToken","deviceSecret","SESSION_HANDOFF_DEADLINE_MS","exports","COLD_BOOT_OVERALL_DEADLINE_MS","OFFLINE_PROBE_TIMEOUT_MS","detectOfflineHint","isWebBrowser","online","navigator","onLine","NetInfo","Promise","resolve","then","state","race","fetch","setTimeout","isNetConnectivityExplicitlyOffline","
|
|
1
|
+
{"version":3,"names":["_applicationSessionLane","require","_core","_session","_deviceCredential","_sharedDeviceCredentialStore","_oauthReturn","_isWebBrowser","_netConnectivity","_interopRequireWildcard","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","hasOAuthReturnOnUrl","search","globalThis","location","params","URLSearchParams","hasRestorableState","store","persisted","load","Boolean","accessToken","deviceSecret","SESSION_HANDOFF_DEADLINE_MS","exports","COLD_BOOT_OVERALL_DEADLINE_MS","OFFLINE_PROBE_TIMEOUT_MS","detectOfflineHint","isWebBrowser","online","navigator","onLine","NetInfo","Promise","resolve","then","state","race","fetch","setTimeout","isNetConnectivityExplicitlyOffline","startClassification","opts","classification","resolveSessionLane","resolveApplicationSessionLane","oxyServices","clientId","catch","undefined","restoreDeviceSession","painted","authStore","sessionMode","identity","sessionClient","syncDeviceCredentialToHost","commitSession","markAuthResolved","beforeDeviceBoot","offline","outcome","holderRejected","runSessionColdBoot","oxy","platform","isWeb","isNative","sharedDeviceCredential","createPlatformSharedDeviceCredentialStore","overallDeadlineMs","isOffline","requireServerVerdict","onHolderRejected","onStepDeadline","stepId","loggerUtil","warn","component","method","onSession","session","handoff","sessionId","userId","deviceState","activate","handoffDeadlineId","finally","clearTimeout","onSignedOut","cred","loadPersistedDeviceCredential","start","socketError","__DEV__","debug","onStepError","id","error","afterDeviceBoot","verifyCachedSession","verified","lane","onCachedSessionRejected","onCachedSessionUnconfirmed","runProviderColdBoot","authRedirectUri","setTokenReady","identityBound","freshLaunch","paintCachedSession","onClassified","oauthCompleted","tryCompleteOAuthReturn","input","oauth","Error","String"],"sourceRoot":"../../../../src","sources":["ui/boot/runProviderColdBoot.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -49,6 +49,7 @@ var _applicationSessionLane = require("../oauth/applicationSessionLane.js");
|
|
|
49
49
|
var _errorHandlers = require("../utils/errorHandlers.js");
|
|
50
50
|
var _deviceCredential = require("../utils/deviceCredential.js");
|
|
51
51
|
var _tokenLossRecovery = require("../session/tokenLossRecovery.js");
|
|
52
|
+
var _cachedBoot = require("../session/cachedBoot.js");
|
|
52
53
|
var _authStore = require("../stores/authStore.js");
|
|
53
54
|
var _useLanguageManagement = require("../hooks/useLanguageManagement.js");
|
|
54
55
|
var _useSessionManagement = require("../hooks/useSessionManagement.js");
|
|
@@ -169,6 +170,7 @@ const OxyRuntimeProvider = ({
|
|
|
169
170
|
clientId: clientIdProp,
|
|
170
171
|
services: oxyServices,
|
|
171
172
|
lane: null,
|
|
173
|
+
provisional: false,
|
|
172
174
|
pending: null
|
|
173
175
|
});
|
|
174
176
|
if (registeredLaneRef.current.clientId !== clientIdProp || registeredLaneRef.current.services !== oxyServices) {
|
|
@@ -176,6 +178,7 @@ const OxyRuntimeProvider = ({
|
|
|
176
178
|
clientId: clientIdProp,
|
|
177
179
|
services: oxyServices,
|
|
178
180
|
lane: null,
|
|
181
|
+
provisional: false,
|
|
179
182
|
pending: null
|
|
180
183
|
};
|
|
181
184
|
}
|
|
@@ -298,6 +301,12 @@ const OxyRuntimeProvider = ({
|
|
|
298
301
|
storageReadyRef.current = buildStorageDeferred();
|
|
299
302
|
}
|
|
300
303
|
const storageReady = storageReadyRef.current;
|
|
304
|
+
|
|
305
|
+
// What a returning session's first paint needs (the app's classification and
|
|
306
|
+
// the account's profile), kept in the same storage as the account query cache.
|
|
307
|
+
const cachedBoot = (0, _react.useMemo)(() => storage ? (0, _cachedBoot.createCachedBootStore)(storage, storageKeyPrefix) : null, [storage, storageKeyPrefix]);
|
|
308
|
+
const cachedBootRef = (0, _react.useRef)(cachedBoot);
|
|
309
|
+
cachedBootRef.current = cachedBoot;
|
|
301
310
|
(0, _react.useEffect)(() => {
|
|
302
311
|
// `null` means the public provider owns initialization and has not resolved
|
|
303
312
|
// it yet. `undefined` means this internal provider was mounted directly and
|
|
@@ -445,10 +454,16 @@ const OxyRuntimeProvider = ({
|
|
|
445
454
|
const selected = registeredLaneRef.current;
|
|
446
455
|
if (selected.clientId !== clientIdProp || selected.services !== oxyServices) throw new Error('Provider classification was superseded');
|
|
447
456
|
if (selected.pending) return selected.pending;
|
|
448
|
-
selected.lane = null;
|
|
457
|
+
if (!selected.provisional) selected.lane = null;
|
|
449
458
|
const pending = (async () => {
|
|
450
459
|
const lane = await (0, _applicationSessionLane.resolveApplicationSessionLane)(oxyServices, clientIdProp);
|
|
451
460
|
if (registeredLaneRef.current !== selected) throw new Error('Provider classification was superseded');
|
|
461
|
+
if (clientIdProp) void cachedBootRef.current?.saveLane(clientIdProp, lane);
|
|
462
|
+
if (selected.provisional) {
|
|
463
|
+
// The registry has answered: its verdict replaces the cached one now.
|
|
464
|
+
selected.provisional = false;
|
|
465
|
+
selected.lane = lane;
|
|
466
|
+
}
|
|
452
467
|
if (lane === 'oauth' && !(0, _isolatedOAuthSession.hasIsolatedOAuthSession)(runtime)) {
|
|
453
468
|
sessionClient.resetLocalState();
|
|
454
469
|
sessionClientHost.setDeviceCredential(null);
|
|
@@ -1101,6 +1116,100 @@ const OxyRuntimeProvider = ({
|
|
|
1101
1116
|
if (!accountDialogOpen) (0, _signInFlowStore.clearSignInFlows)(accountDialogControllerRef.current);
|
|
1102
1117
|
}, [accountDialogOpen]);
|
|
1103
1118
|
|
|
1119
|
+
// ── Cached first paint ───────────────────────────────────────────────────
|
|
1120
|
+
// Keep the profile the runtime holds for the signed-in account, so the next
|
|
1121
|
+
// cold boot can paint it before the network answers. Only for a device-lane
|
|
1122
|
+
// account session: an identity-bound client and an isolated OAuth session
|
|
1123
|
+
// always boot through the network. A sign-out deletes it.
|
|
1124
|
+
const cachedAccountJsonRef = (0, _react.useRef)(null);
|
|
1125
|
+
(0, _react.useEffect)(() => {
|
|
1126
|
+
if (!cachedBoot || identity) return;
|
|
1127
|
+
if (user === null) {
|
|
1128
|
+
if (cachedAccountJsonRef.current !== null) {
|
|
1129
|
+
cachedAccountJsonRef.current = null;
|
|
1130
|
+
void cachedBoot.clearAccount();
|
|
1131
|
+
}
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
if (registeredLaneRef.current.lane !== 'device' || (0, _isolatedOAuthSession.hasIsolatedOAuthSession)(runtime)) return;
|
|
1135
|
+
const json = JSON.stringify(user);
|
|
1136
|
+
if (json === cachedAccountJsonRef.current) return;
|
|
1137
|
+
cachedAccountJsonRef.current = json;
|
|
1138
|
+
void cachedBoot.saveAccount(user);
|
|
1139
|
+
}, [cachedBoot, identity, user, runtime]);
|
|
1140
|
+
|
|
1141
|
+
// Paint the session saved on this device: the bearer it left (when still
|
|
1142
|
+
// valid), the session mirror, the account from its cached profile, auth
|
|
1143
|
+
// resolved. Local reads only; the boot checks it all in the background.
|
|
1144
|
+
const paintCachedSession = (0, _react.useCallback)(async () => {
|
|
1145
|
+
const cache = cachedBootRef.current;
|
|
1146
|
+
if (identity || !clientIdProp || !cache) return false;
|
|
1147
|
+
const cached = await cache.readSession(clientIdProp, authStore);
|
|
1148
|
+
const selected = registeredLaneRef.current;
|
|
1149
|
+
if (!cached || selected.clientId !== clientIdProp || selected.services !== oxyServices || selected.lane === 'oauth') {
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
if (selected.lane === null) {
|
|
1153
|
+
// The cached verdict stands in for the registry until it answers, so the
|
|
1154
|
+
// refresh handler, lost-token recovery and sign-in treat this app as the
|
|
1155
|
+
// device lane it was on last run.
|
|
1156
|
+
selected.lane = 'device';
|
|
1157
|
+
selected.provisional = true;
|
|
1158
|
+
}
|
|
1159
|
+
if (cached.warmToken) oxyServices.session.setAccessToken(cached.warmToken);
|
|
1160
|
+
await syncDeviceCredentialToHost();
|
|
1161
|
+
const now = new Date();
|
|
1162
|
+
runtime.batch(() => {
|
|
1163
|
+
runtime.mergeSessions([{
|
|
1164
|
+
sessionId: cached.sessionId,
|
|
1165
|
+
deviceId: cached.deviceId,
|
|
1166
|
+
expiresAt: new Date(now.getTime() + _oxyContextHelpers.DEFAULT_SESSION_VALIDITY_MS).toISOString(),
|
|
1167
|
+
lastActive: now.toISOString(),
|
|
1168
|
+
userId: cached.userId,
|
|
1169
|
+
isCurrent: true
|
|
1170
|
+
}], {
|
|
1171
|
+
merge: true
|
|
1172
|
+
});
|
|
1173
|
+
runtime.setActiveSessionId(cached.sessionId);
|
|
1174
|
+
runtime.setAccount(cached.user);
|
|
1175
|
+
});
|
|
1176
|
+
onAuthStateChangeRef.current?.(cached.user);
|
|
1177
|
+
markAuthResolvedRef.current();
|
|
1178
|
+
return true;
|
|
1179
|
+
}, [identity, clientIdProp, authStore, oxyServices, runtime, syncDeviceCredentialToHost]);
|
|
1180
|
+
|
|
1181
|
+
// A painted session the server could not be asked about yet stays on screen
|
|
1182
|
+
// and is asked again: on the lost-token backoff, and at once when the device
|
|
1183
|
+
// comes back online. Signed out meanwhile, there is nothing left to ask.
|
|
1184
|
+
const cachedSessionCheckRef = (0, _react.useRef)({
|
|
1185
|
+
timer: null,
|
|
1186
|
+
attempt: 0,
|
|
1187
|
+
runNow: null
|
|
1188
|
+
});
|
|
1189
|
+
const scheduleCachedSessionCheck = (0, _react.useCallback)(retry => {
|
|
1190
|
+
const check = cachedSessionCheckRef.current;
|
|
1191
|
+
if (check.timer !== null) clearTimeout(check.timer);
|
|
1192
|
+
const run = () => {
|
|
1193
|
+
if (check.timer !== null) clearTimeout(check.timer);
|
|
1194
|
+
check.timer = null;
|
|
1195
|
+
check.runNow = null;
|
|
1196
|
+
if (runtime.getSnapshot().account === null) return;
|
|
1197
|
+
retry().catch(retryError => {
|
|
1198
|
+
logger('Failed to check the cached session', retryError);
|
|
1199
|
+
});
|
|
1200
|
+
};
|
|
1201
|
+
const delay = _tokenLossRecovery.TOKEN_RECOVERY_BACKOFF_MS[Math.min(check.attempt, _tokenLossRecovery.TOKEN_RECOVERY_BACKOFF_MS.length - 1)];
|
|
1202
|
+
check.attempt += 1;
|
|
1203
|
+
check.runNow = run;
|
|
1204
|
+
check.timer = setTimeout(run, delay);
|
|
1205
|
+
}, [runtime, logger]);
|
|
1206
|
+
(0, _react.useEffect)(() => () => {
|
|
1207
|
+
const check = cachedSessionCheckRef.current;
|
|
1208
|
+
if (check.timer !== null) clearTimeout(check.timer);
|
|
1209
|
+
check.timer = null;
|
|
1210
|
+
check.runNow = null;
|
|
1211
|
+
}, []);
|
|
1212
|
+
|
|
1104
1213
|
// ── Cold boot ────────────────────────────────────────────────────────────
|
|
1105
1214
|
// Device-first session restore via `runProviderColdBoot` (see boot/runProviderColdBoot.ts).
|
|
1106
1215
|
const runColdBoot = (0, _react.useCallback)(async () => {
|
|
@@ -1126,12 +1235,15 @@ const OxyRuntimeProvider = ({
|
|
|
1126
1235
|
syncDeviceCredentialToHost,
|
|
1127
1236
|
commitSession: (input, options) => commitSessionRef.current(input, options),
|
|
1128
1237
|
markAuthResolved: () => markAuthResolvedRef.current(),
|
|
1129
|
-
setTokenReady
|
|
1238
|
+
setTokenReady,
|
|
1239
|
+
paintCachedSession,
|
|
1240
|
+
onCachedSessionRejected: () => clearSessionStateRef.current(),
|
|
1241
|
+
onCachedSessionUnconfirmed: scheduleCachedSessionCheck
|
|
1130
1242
|
});
|
|
1131
1243
|
// The boot may have (re-)established the identity session or cleared a pin
|
|
1132
1244
|
// whose key no longer exists, so re-resolve rather than trusting the memo
|
|
1133
1245
|
// taken before it ran.
|
|
1134
|
-
}, [resolveRegisteredLane, oxyServices, authStore, clientIdProp, authRedirectUri, sessionMode, identity, syncDeviceCredentialToHost, sessionClient, setTokenReady]);
|
|
1246
|
+
}, [resolveRegisteredLane, oxyServices, authStore, clientIdProp, authRedirectUri, sessionMode, identity, syncDeviceCredentialToHost, sessionClient, setTokenReady, paintCachedSession, scheduleCachedSessionCheck]);
|
|
1135
1247
|
(0, _react.useEffect)(() => {
|
|
1136
1248
|
if (initialized) {
|
|
1137
1249
|
return;
|
|
@@ -1214,6 +1326,7 @@ const OxyRuntimeProvider = ({
|
|
|
1214
1326
|
if (!online || !wasOffline) {
|
|
1215
1327
|
return;
|
|
1216
1328
|
}
|
|
1329
|
+
cachedSessionCheckRef.current.runNow?.();
|
|
1217
1330
|
void (async () => {
|
|
1218
1331
|
if ((0, _isolatedOAuthSession.hasIsolatedOAuthSession)(runtime)) return;
|
|
1219
1332
|
if (oxyServices.session.accessToken || !sessionClientHost.getDeviceCredential()) {
|