fix(core): token race condition (#15320)

fix #15318
fix #15310

#### PR Dependency Tree


* **PR #15320** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Authenticated sessions are now restored automatically when the desktop
app starts.
* Previously saved access tokens are available immediately for
recognized endpoints.
* A problem initializing one saved session no longer prevents other
sessions from loading.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-07-22 21:42:23 +08:00
committed by GitHub
parent 4b37f9d42e
commit 02e75862cc
3 changed files with 40 additions and 2 deletions
@@ -143,6 +143,16 @@ export function getAuthSessionBroker(endpoint: string) {
return broker;
}
export async function initializeAuthSessions() {
for (const endpoint of Object.keys(await readStore())) {
try {
getAuthSessionBroker(endpoint);
} catch (error) {
logger.error('failed to initialize auth session', endpoint, error);
}
}
}
export function isManagedAuthEndpoint(url: string) {
try {
const parsed = new URL(url);
@@ -14,6 +14,7 @@ import {
import {
executeAuthSessionRequest,
getAccessTokenForUrl,
initializeAuthSessions,
isManagedAuthEndpoint,
} from './auth/auth-session';
import { buildType, isDev } from './config';
@@ -213,7 +214,9 @@ function allowCors(
);
}
export function registerProtocol() {
export async function registerProtocol() {
await initializeAuthSessions();
protocol.handle('assets', request => {
return handleFileRequest(request);
});
@@ -64,6 +64,8 @@ import {
clearAuthSession,
executeAuthSessionRequest,
getValidAccessToken,
initializeAuthSessions,
isManagedAuthEndpoint,
normalizeEndpoint,
revokeAuthSession,
setAuthSession,
@@ -87,9 +89,32 @@ test.each([
expect(normalizeEndpoint(endpoint)).toBe(expected);
});
test('atomically persists one encrypted token-pair record', async () => {
test('loads and atomically persists one encrypted token-pair record', async () => {
const endpoint = 'https://persistent.example';
const pair = tokenResponse('access-persistent', 'p', 900);
runtime.files.set(
sessionFile,
JSON.stringify({
[endpoint]: Buffer.from(
JSON.stringify({
version: 1,
tokenType: pair.tokenType,
accessToken: pair.accessToken,
accessExpiresAt: new Date(
Date.now() + pair.expiresIn * 1000
).toISOString(),
refreshToken: pair.refreshToken,
refreshExpiresAt: pair.refreshExpiresAt,
session: pair.session,
})
).toString('base64'),
})
);
expect(isManagedAuthEndpoint(endpoint)).toBe(false);
await initializeAuthSessions();
expect(isManagedAuthEndpoint(endpoint)).toBe(true);
expect(await getValidAccessToken(endpoint)).toBe('access-persistent');
await expect(setAuthSession(endpoint, pair)).resolves.toEqual({
persistent: true,