You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -337,15 +336,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
337
336
currentProjectId: number|null;
338
337
},
339
338
): Promise<void>{
340
-
// Serialize commits onto a chain. Encryption is async, so two overlapping
341
-
// selections would otherwise interleave across the await — an earlier one
342
-
// completing last would clobber the newer stored session and published
343
-
// state (and persistSession's own read of the prior selection would race).
344
-
// Chaining runs each commit's persist-then-publish to completion before the
345
-
// next starts, so the latest selection wins consistently across the
346
-
// in-memory session, storage, and subscribers. The stored chain swallows
347
-
// rejections so one failed commit doesn't wedge later ones; the returned
348
-
// promise still rejects for the caller.
339
+
// Serialize commits onto a chain so overlapping selections can't interleave across async encryption and clobber a newer one; the latest wins across session/storage/subscribers, and the stored chain swallows rejections (so one failure doesn't wedge later commits) while the returned promise still rejects for the caller.
349
340
construn=this.commitChain.then(()=>
350
341
this.applyCommittedSession(prevSession,next),
351
342
);
@@ -368,12 +359,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
368
359
orgProjectsIncomplete: false,
369
360
};
370
361
371
-
// Persist the durable session first — it's the only step here that can
372
-
// fail (encryption is async and may reject). Mutating this.session, the
373
-
// project preference, or the published state before it would strand the
374
-
// service on a project change that the stored session and UI never
375
-
// committed. Commit those only after the persist resolves, so a rejection
376
-
// leaves every layer on the prior session.
362
+
// Persist the durable session first — the only step that can fail (async encryption may reject) — and mutate this.session, the project preference, and published state only after it resolves, so a rejection leaves every layer on the prior session.
377
363
awaitthis.persistSession({
378
364
refreshToken: nextSession.refreshToken,
379
365
cloudRegion: nextSession.cloudRegion,
@@ -533,13 +519,7 @@ export class AuthService extends TypedEventEmitter<AuthServiceEvents> {
533
519
returnthis.refreshPromise;
534
520
}
535
521
536
-
// Assign refreshPromise synchronously — with no await between the guard
537
-
// above and this assignment — so concurrent callers dedupe onto one
538
-
// refresh. Resolving the stored session (which now awaits decryption)
539
-
// must therefore happen INSIDE refreshAndSync, not before it; otherwise
540
-
// two callers both pass the null guard, both decrypt, and both fire a
541
-
// refresh, burning the rotating refresh token twice and logging the user
542
-
// out when the second request fails.
522
+
// Assign refreshPromise synchronously — no await between the guard above and this assignment — so concurrent callers dedupe onto one refresh; resolving the stored session (now async) must therefore happen INSIDE refreshAndSync, else two callers both refresh, burning the rotating token twice and logging the user out.
// Claim the recovery slot synchronously so concurrent triggers (network
1122
-
// regained + tab resume) don't both kick a token refresh; decryptability
1123
-
// is now async (Web Crypto), so it's validated inside recoverSession.
1101
+
// Claim the recovery slot synchronously so concurrent triggers (network regained + tab resume) don't both kick a token refresh; decryptability is now async (Web Crypto), so it's validated inside recoverSession.
0 commit comments