Skip to content

Commit 7327980

Browse files
committed
Update authStore.ts
1 parent 122244e commit 7327980

File tree

1 file changed

+108
-93
lines changed

1 file changed

+108
-93
lines changed

apps/array/src/renderer/features/auth/stores/authStore.ts

Lines changed: 108 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ANALYTICS_EVENTS } from "@/types/analytics";
1717
const log = logger.scope("auth-store");
1818

1919
let refreshPromise: Promise<void> | null = null;
20+
let initializePromise: Promise<boolean> | null = null;
2021

2122
const REFRESH_MAX_RETRIES = 3;
2223
const REFRESH_INITIAL_DELAY_MS = 1000;
@@ -305,124 +306,138 @@ export const useAuthStore = create<AuthState>()(
305306
},
306307

307308
initializeOAuth: async () => {
308-
// Wait for zustand hydration from async storage
309-
if (!useAuthStore.persist.hasHydrated()) {
310-
await new Promise<void>((resolve) => {
311-
useAuthStore.persist.onFinishHydration(() => resolve());
312-
});
309+
// If initialization is already in progress, wait for it
310+
if (initializePromise) {
311+
log.debug("OAuth initialization already in progress, waiting...");
312+
return initializePromise;
313313
}
314314

315-
const state = get();
316-
317-
if (state.storedTokens) {
318-
const tokens = state.storedTokens;
319-
const now = Date.now();
320-
const isExpired = tokens.expiresAt <= now;
321-
322-
set({
323-
oauthAccessToken: tokens.accessToken,
324-
oauthRefreshToken: tokens.refreshToken,
325-
tokenExpiry: tokens.expiresAt,
326-
cloudRegion: tokens.cloudRegion,
327-
});
328-
329-
if (isExpired) {
330-
try {
331-
await get().refreshAccessToken();
332-
} catch (error) {
333-
log.error("Failed to refresh expired token:", error);
334-
set({ storedTokens: null, isAuthenticated: false });
335-
return false;
336-
}
315+
const doInitialize = async (): Promise<boolean> => {
316+
// Wait for zustand hydration from async storage
317+
if (!useAuthStore.persist.hasHydrated()) {
318+
await new Promise<void>((resolve) => {
319+
useAuthStore.persist.onFinishHydration(() => resolve());
320+
});
337321
}
338322

339-
// Re-fetch tokens after potential refresh to get updated values
340-
const currentTokens = get().storedTokens;
341-
if (!currentTokens) {
342-
return false;
343-
}
323+
const state = get();
344324

345-
const apiHost = getCloudUrlFromRegion(currentTokens.cloudRegion);
346-
const projectId = currentTokens.scopedTeams?.[0];
325+
if (state.storedTokens) {
326+
const tokens = state.storedTokens;
327+
const now = Date.now();
328+
const isExpired = tokens.expiresAt <= now;
347329

348-
if (!projectId) {
349-
log.error("No project ID found in stored tokens");
350-
get().logout();
351-
return false;
352-
}
330+
set({
331+
oauthAccessToken: tokens.accessToken,
332+
oauthRefreshToken: tokens.refreshToken,
333+
tokenExpiry: tokens.expiresAt,
334+
cloudRegion: tokens.cloudRegion,
335+
});
353336

354-
const client = new PostHogAPIClient(
355-
currentTokens.accessToken,
356-
apiHost,
357-
async () => {
358-
await get().refreshAccessToken();
359-
const token = get().oauthAccessToken;
360-
if (!token) {
361-
throw new Error("No access token after refresh");
337+
if (isExpired) {
338+
try {
339+
await get().refreshAccessToken();
340+
} catch (error) {
341+
log.error("Failed to refresh expired token:", error);
342+
set({ storedTokens: null, isAuthenticated: false });
343+
return false;
362344
}
363-
return token;
364-
},
365-
projectId,
366-
);
367-
368-
try {
369-
const user = await client.getCurrentUser();
345+
}
370346

371-
set({
372-
isAuthenticated: true,
373-
client,
374-
projectId,
375-
});
347+
// Re-fetch tokens after potential refresh to get updated values
348+
const currentTokens = get().storedTokens;
349+
if (!currentTokens) {
350+
return false;
351+
}
376352

377-
get().scheduleTokenRefresh();
353+
const apiHost = getCloudUrlFromRegion(currentTokens.cloudRegion);
354+
const projectId = currentTokens.scopedTeams?.[0];
378355

379-
// Use distinct_id to match web sessions (same as PostHog web app)
380-
const distinctId = user.distinct_id || user.email;
381-
identifyUser(distinctId, {
382-
email: user.email,
383-
uuid: user.uuid,
384-
project_id: projectId.toString(),
385-
region: tokens.cloudRegion,
386-
});
356+
if (!projectId) {
357+
log.error("No project ID found in stored tokens");
358+
get().logout();
359+
return false;
360+
}
387361

388-
trpcVanilla.analytics.setUserId.mutate({
389-
userId: distinctId,
390-
properties: {
391-
email: user.email,
392-
uuid: user.uuid,
393-
project_id: projectId.toString(),
394-
region: tokens.cloudRegion,
362+
const client = new PostHogAPIClient(
363+
currentTokens.accessToken,
364+
apiHost,
365+
async () => {
366+
await get().refreshAccessToken();
367+
const token = get().oauthAccessToken;
368+
if (!token) {
369+
throw new Error("No access token after refresh");
370+
}
371+
return token;
395372
},
396-
});
397-
398-
return true;
399-
} catch (error) {
400-
log.error("Failed to validate OAuth session:", error);
373+
projectId,
374+
);
401375

402-
// Network errors from fetch are TypeError, wrapped by fetcher.ts as cause
403-
const isNetworkError =
404-
error instanceof Error && error.cause instanceof TypeError;
376+
try {
377+
const user = await client.getCurrentUser();
405378

406-
if (isNetworkError) {
407-
log.warn(
408-
"Network error during session validation - keeping session active",
409-
);
410379
set({
411380
isAuthenticated: true,
412381
client,
413382
projectId,
414383
});
384+
415385
get().scheduleTokenRefresh();
386+
387+
// Use distinct_id to match web sessions (same as PostHog web app)
388+
const distinctId = user.distinct_id || user.email;
389+
identifyUser(distinctId, {
390+
email: user.email,
391+
uuid: user.uuid,
392+
project_id: projectId.toString(),
393+
region: tokens.cloudRegion,
394+
});
395+
396+
trpcVanilla.analytics.setUserId.mutate({
397+
userId: distinctId,
398+
properties: {
399+
email: user.email,
400+
uuid: user.uuid,
401+
project_id: projectId.toString(),
402+
region: tokens.cloudRegion,
403+
},
404+
});
405+
416406
return true;
417-
}
407+
} catch (error) {
408+
log.error("Failed to validate OAuth session:", error);
409+
410+
// Network errors from fetch are TypeError, wrapped by fetcher.ts as cause
411+
const isNetworkError =
412+
error instanceof Error && error.cause instanceof TypeError;
413+
414+
if (isNetworkError) {
415+
log.warn(
416+
"Network error during session validation - keeping session active",
417+
);
418+
set({
419+
isAuthenticated: true,
420+
client,
421+
projectId,
422+
});
423+
get().scheduleTokenRefresh();
424+
return true;
425+
}
418426

419-
// For auth errors (401/403) or unknown errors, clear the session
420-
set({ storedTokens: null, isAuthenticated: false });
421-
return false;
427+
// For auth errors (401/403) or unknown errors, clear the session
428+
set({ storedTokens: null, isAuthenticated: false });
429+
return false;
430+
}
422431
}
423-
}
424432

425-
return state.isAuthenticated;
433+
return state.isAuthenticated;
434+
};
435+
436+
initializePromise = doInitialize().finally(() => {
437+
initializePromise = null;
438+
});
439+
440+
return initializePromise;
426441
},
427442

428443
logout: () => {

0 commit comments

Comments
 (0)