From 1561890125074397b3196f0fee68afb63ee24a05 Mon Sep 17 00:00:00 2001 From: shanu Date: Thu, 16 Apr 2026 18:28:19 +0530 Subject: [PATCH 1/5] feat(deepLink): implement deep link authentication state management - Added a new module for managing deep link authentication state, including processing states and error handling. - Integrated deep link authentication state management into the desktop deep link listener, enhancing the handling of authentication flows. - Introduced functions to begin, complete, and fail deep link authentication processing, improving user feedback during the authentication process. --- app/src/store/deepLinkAuthState.ts | 46 ++++++++++++++++++++++++ app/src/utils/desktopDeepLinkListener.ts | 44 +++++++++++++++++------ 2 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 app/src/store/deepLinkAuthState.ts diff --git a/app/src/store/deepLinkAuthState.ts b/app/src/store/deepLinkAuthState.ts new file mode 100644 index 0000000000..cab3e8cb11 --- /dev/null +++ b/app/src/store/deepLinkAuthState.ts @@ -0,0 +1,46 @@ +import { useSyncExternalStore } from 'react'; + +export interface DeepLinkAuthState { + isProcessing: boolean; + errorMessage: string | null; +} + +const initialState: DeepLinkAuthState = { isProcessing: false, errorMessage: null }; + +let deepLinkAuthState: DeepLinkAuthState = initialState; +const listeners = new Set<() => void>(); + +const emitChange = (): void => { + for (const listener of listeners) { + listener(); + } +}; + +const setDeepLinkAuthState = (next: DeepLinkAuthState): void => { + deepLinkAuthState = next; + emitChange(); +}; + +export const getDeepLinkAuthState = (): DeepLinkAuthState => deepLinkAuthState; + +export const subscribeDeepLinkAuthState = (listener: () => void): (() => void) => { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; +}; + +export const beginDeepLinkAuthProcessing = (): void => { + setDeepLinkAuthState({ isProcessing: true, errorMessage: null }); +}; + +export const completeDeepLinkAuthProcessing = (): void => { + setDeepLinkAuthState({ isProcessing: false, errorMessage: null }); +}; + +export const failDeepLinkAuthProcessing = (message: string): void => { + setDeepLinkAuthState({ isProcessing: false, errorMessage: message }); +}; + +export const useDeepLinkAuthState = (): DeepLinkAuthState => + useSyncExternalStore(subscribeDeepLinkAuthState, getDeepLinkAuthState, getDeepLinkAuthState); diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index 1cf42c3bb9..b9c8211e8c 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -2,9 +2,14 @@ import { isTauri as coreIsTauri } from '@tauri-apps/api/core'; import { getCurrentWindow } from '@tauri-apps/api/window'; import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link'; -import { getCoreStateSnapshot } from '../lib/coreState/store'; +import { getCoreStateSnapshot, patchCoreStateSnapshot } from '../lib/coreState/store'; import { consumeLoginToken } from '../services/api/authApi'; import { buildManualSentryEvent, enqueueError } from '../services/errorReportQueue'; +import { + beginDeepLinkAuthProcessing, + completeDeepLinkAuthProcessing, + failDeepLinkAuthProcessing, +} from '../store/deepLinkAuthState'; import { evaluateOAuthAppVersionGate } from './oauthAppVersionGate'; import { openUrl } from './openUrl'; import { storeSession } from './tauriCommands'; @@ -44,26 +49,43 @@ const handleAuthDeepLink = async (parsed: URL) => { const key = parsed.searchParams.get('key'); if (!token) { console.warn('[DeepLink] URL did not contain a token query parameter'); + failDeepLinkAuthProcessing('Sign-in callback was missing a token. Please try again.'); return; } + beginDeepLinkAuthProcessing(); + console.log('[DeepLink][auth] received', { tokenLength: token.length, keyMode: parsed.searchParams.get('key') ?? 'consume', }); - await focusMainWindow(); - await waitForAuthReadiness(); + try { + await focusMainWindow(); + await waitForAuthReadiness(); + + if (key === 'auth') { + await storeSession(token, {}); + patchCoreStateSnapshot({ snapshot: { sessionToken: token } }); + window.dispatchEvent( + new CustomEvent('core-state:session-token-updated', { detail: { sessionToken: token } }) + ); + console.log('[DeepLink][auth] bypass token applied'); + } else { + const jwtToken = await consumeLoginToken(token); + await storeSession(jwtToken, {}); + patchCoreStateSnapshot({ snapshot: { sessionToken: jwtToken } }); + window.dispatchEvent( + new CustomEvent('core-state:session-token-updated', { detail: { sessionToken: jwtToken } }) + ); + console.log('[DeepLink][auth] login token consumed'); + } - if (key === 'auth') { - await storeSession(token, {}); - console.log('[DeepLink][auth] bypass token applied'); - window.location.hash = '/home'; - } else { - const jwtToken = await consumeLoginToken(token); - await storeSession(jwtToken, {}); - console.log('[DeepLink][auth] login token consumed'); window.location.hash = '/home'; + completeDeepLinkAuthProcessing(); + } catch (error) { + console.error('[DeepLink][auth] failed to complete login:', error); + failDeepLinkAuthProcessing('Sign-in failed. Please try again.'); } }; From 7af774dc27da6c716c82ec97e752a426f0c6047f Mon Sep 17 00:00:00 2001 From: shanu Date: Thu, 16 Apr 2026 18:29:44 +0530 Subject: [PATCH 2/5] refactor(settings): simplify logout process and enhance error handling - Removed onboarding completion flag from the logout process to streamline functionality. - Improved error handling during logout to provide user feedback if the logout fails. - Updated comments to clarify the behavior of session clearing and routing after logout. - Integrated deep link authentication state management into the settings component for better user experience. --- app/src/components/settings/SettingsHome.tsx | 16 ++-- app/src/pages/Welcome.tsx | 40 +++++++--- app/src/providers/CoreStateProvider.tsx | 78 +++++++++++++++++++- 3 files changed, 109 insertions(+), 25 deletions(-) diff --git a/app/src/components/settings/SettingsHome.tsx b/app/src/components/settings/SettingsHome.tsx index 4cc8df29dc..a2a52daaf3 100644 --- a/app/src/components/settings/SettingsHome.tsx +++ b/app/src/components/settings/SettingsHome.tsx @@ -9,23 +9,22 @@ import { useSettingsNavigation } from './hooks/useSettingsNavigation'; const SettingsHome = () => { const { navigateToSettings } = useSettingsNavigation(); - const { clearSession, setOnboardingCompletedFlag } = useCoreState(); + const { clearSession } = useCoreState(); const [showLogoutAndClearModal, setShowLogoutAndClearModal] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const handleLogout = async () => { - try { - await setOnboardingCompletedFlag(false); - } catch (err) { - console.warn('[Settings] Failed to clear onboarding_completed in config:', err); - } + let logoutSucceeded = true; try { await clearSession(); } catch (err) { + logoutSucceeded = false; console.warn('[Settings] Rust logout failed:', err); } - window.location.hash = '/'; + if (!logoutSucceeded) { + setError('Failed to log out. Please try again.'); + } }; const clearAllAppData = async () => { @@ -46,8 +45,7 @@ const SettingsHome = () => { window.localStorage.clear(); window.sessionStorage.clear(); - // Complete reset - redirect to login for fresh start - window.location.hash = '/'; + // Route guards will redirect signed-out users to the public entry route. }; const handleLogoutAndClearData = async () => { diff --git a/app/src/pages/Welcome.tsx b/app/src/pages/Welcome.tsx index 5e1be5e349..7ab9e29adf 100644 --- a/app/src/pages/Welcome.tsx +++ b/app/src/pages/Welcome.tsx @@ -1,8 +1,11 @@ import OAuthProviderButton from '../components/oauth/OAuthProviderButton'; import { oauthProviderConfigs } from '../components/oauth/providerConfigs'; import RotatingTetrahedronCanvas from '../components/RotatingTetrahedronCanvas'; +import { useDeepLinkAuthState } from '../store/deepLinkAuthState'; const Welcome = () => { + const { isProcessing, errorMessage } = useDeepLinkAuthState(); + return (
@@ -25,18 +28,31 @@ const Welcome = () => { AI Super Intelligence. Private, Simple and extremely powerful.

- {/* OAuth buttons — horizontal row */} -
- {oauthProviderConfigs - .filter(p => ['google', 'github', 'twitter'].includes(p.id)) - .map(provider => ( - - ))} -
+ {errorMessage ? ( +
+ {errorMessage} +
+ ) : null} + + {isProcessing ? ( +
+
+

Signing you in...

+
+ ) : ( + /* OAuth buttons — horizontal row */ +
+ {oauthProviderConfigs + .filter(p => ['google', 'github', 'twitter'].includes(p.id)) + .map(provider => ( + + ))} +
+ )} {/* Email login — disabled until backend auth flow is implemented
diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index 38c58221d8..ecf6bc2793 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -102,6 +102,7 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) const snapshotRequestIdRef = useRef(0); const teamsRequestIdRef = useRef(0); const memoryTokenRef = useRef(state.snapshot.sessionToken); + const logoutGuardUntilRef = useRef(0); const bootstrapFailCountRef = useRef(0); const refreshInFlightRef = useRef | null>(null); @@ -116,22 +117,44 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) const refreshCore = useCallback(async () => { const requestId = ++snapshotRequestIdRef.current; const snapshot = normalizeSnapshot(await fetchCoreAppSnapshot()); + if (!snapshot.sessionToken) { + logoutGuardUntilRef.current = 0; + } commitState(previous => { if (requestId !== snapshotRequestIdRef.current) { return previous; } + const withinLogoutGuardWindow = Date.now() < logoutGuardUntilRef.current; + const shouldSuppressTokenRehydrate = + withinLogoutGuardWindow && + !previous.snapshot.sessionToken && + Boolean(snapshot.sessionToken); + const nextSnapshot = shouldSuppressTokenRehydrate + ? { + ...snapshot, + auth: { isAuthenticated: false, userId: null, user: null, profileId: null }, + sessionToken: null, + currentUser: null, + onboardingCompleted: false, + } + : snapshot; + const previousIdentity = snapshotIdentity(previous.snapshot); - const nextIdentity = snapshotIdentity(snapshot); + const nextIdentity = snapshotIdentity(nextSnapshot); const shouldClearScopedCaches = previousIdentity !== nextIdentity || - (previous.snapshot.auth.isAuthenticated && !snapshot.auth.isAuthenticated); + (previous.snapshot.auth.isAuthenticated && !nextSnapshot.auth.isAuthenticated); + + if (shouldSuppressTokenRehydrate) { + log('[logout] suppressed stale token during post-logout guard window'); + } return { ...previous, isBootstrapping: false, isReady: true, - snapshot, + snapshot: nextSnapshot, teams: shouldClearScopedCaches ? [] : previous.teams, teamMembersById: shouldClearScopedCaches ? {} : previous.teamMembersById, teamInvitesById: shouldClearScopedCaches ? {} : previous.teamInvitesById, @@ -269,6 +292,48 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) }; }, [commitState, refresh, refreshTeams]); + useEffect(() => { + const onSessionTokenUpdated = (event: Event) => { + const customEvent = event as CustomEvent<{ sessionToken?: string | null }>; + const token = customEvent.detail?.sessionToken; + if (!token) { + return; + } + + // Invalidate stale in-flight snapshot refreshes that started pre-login. + snapshotRequestIdRef.current += 1; + logoutGuardUntilRef.current = 0; + log('[deep-link-auth] optimistic session token sync from deep-link event'); + + memoryTokenRef.current = token; + commitState(previous => ({ + ...previous, + isBootstrapping: false, + isReady: true, + snapshot: { + ...previous.snapshot, + auth: { ...previous.snapshot.auth, isAuthenticated: true }, + sessionToken: token, + }, + })); + + void refresh().catch(err => { + log('refresh failed after deep-link session update: %O', sanitizeError(err)); + }); + }; + + window.addEventListener( + 'core-state:session-token-updated', + onSessionTokenUpdated as EventListener + ); + return () => { + window.removeEventListener( + 'core-state:session-token-updated', + onSessionTokenUpdated as EventListener + ); + }; + }, [commitState, refresh]); + const setAnalyticsEnabled = useCallback( async (enabled: boolean) => { await openhumanUpdateAnalyticsSettings({ enabled }); @@ -312,6 +377,7 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) const storeSessionToken = useCallback( async (token: string, user?: object) => { + logoutGuardUntilRef.current = 0; await storeSession(token, user ?? {}); try { await syncMemoryClientToken(token); @@ -328,12 +394,15 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) ); const clearSession = useCallback(async () => { + // Ignore stale auth snapshots for a short window immediately after logout + // to prevent token "resurrection" flicker in route guards. + logoutGuardUntilRef.current = Date.now() + 5_000; + // Bump the snapshot request counter before doing anything else so that // any snapshot poll already in flight when the user clicked logout is // invalidated on return — otherwise its stale "authenticated" result // would clobber our cleared state a few hundred ms after logout. snapshotRequestIdRef.current += 1; - await tauriLogout(); // Optimistic local clear for instant UI response. commitState(previous => ({ ...previous, @@ -349,6 +418,7 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) }, })); memoryTokenRef.current = null; + await tauriLogout(); // Re-pull the authoritative snapshot from the core so the frontend // cache matches whatever the core now reports. This mirrors the pattern // used by storeSessionToken and ensures any downstream consumer reading From 351b356c3906dd01486a19cb735f2c75a65ab314 Mon Sep 17 00:00:00 2001 From: shanu Date: Thu, 16 Apr 2026 18:33:15 +0530 Subject: [PATCH 3/5] refactor(settings, deepLink): enhance logout and session management - Simplified the logout process by removing unnecessary flags and improving error handling. - Introduced a new function to manage signed-out state in the core state provider. - Streamlined session token application in the deep link listener for better authentication flow. - Updated comments for clarity on session clearing and routing behavior post-logout. --- app/src/components/settings/SettingsHome.tsx | 6 --- app/src/providers/CoreStateProvider.tsx | 51 ++++++-------------- app/src/utils/desktopDeepLinkListener.ts | 35 ++++++-------- 3 files changed, 28 insertions(+), 64 deletions(-) diff --git a/app/src/components/settings/SettingsHome.tsx b/app/src/components/settings/SettingsHome.tsx index a2a52daaf3..f261275d4c 100644 --- a/app/src/components/settings/SettingsHome.tsx +++ b/app/src/components/settings/SettingsHome.tsx @@ -15,14 +15,10 @@ const SettingsHome = () => { const [error, setError] = useState(null); const handleLogout = async () => { - let logoutSucceeded = true; try { await clearSession(); } catch (err) { - logoutSucceeded = false; console.warn('[Settings] Rust logout failed:', err); - } - if (!logoutSucceeded) { setError('Failed to log out. Please try again.'); } }; @@ -44,8 +40,6 @@ const SettingsHome = () => { await persistor.purge(); window.localStorage.clear(); window.sessionStorage.clear(); - - // Route guards will redirect signed-out users to the public entry route. }; const handleLogoutAndClearData = async () => { diff --git a/app/src/providers/CoreStateProvider.tsx b/app/src/providers/CoreStateProvider.tsx index ecf6bc2793..1220d1da99 100644 --- a/app/src/providers/CoreStateProvider.tsx +++ b/app/src/providers/CoreStateProvider.tsx @@ -97,6 +97,16 @@ function normalizeSnapshot( }; } +function toSignedOutSnapshot(snapshot: CoreAppSnapshot): CoreAppSnapshot { + return { + ...snapshot, + auth: { isAuthenticated: false, userId: null, user: null, profileId: null }, + sessionToken: null, + currentUser: null, + onboardingCompleted: false, + }; +} + export default function CoreStateProvider({ children }: { children: ReactNode }) { const [state, setState] = useState(() => getCoreStateSnapshot()); const snapshotRequestIdRef = useRef(0); @@ -125,20 +135,11 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) return previous; } - const withinLogoutGuardWindow = Date.now() < logoutGuardUntilRef.current; - const shouldSuppressTokenRehydrate = - withinLogoutGuardWindow && + const shouldIgnoreTokenDuringLogout = + Date.now() < logoutGuardUntilRef.current && !previous.snapshot.sessionToken && Boolean(snapshot.sessionToken); - const nextSnapshot = shouldSuppressTokenRehydrate - ? { - ...snapshot, - auth: { isAuthenticated: false, userId: null, user: null, profileId: null }, - sessionToken: null, - currentUser: null, - onboardingCompleted: false, - } - : snapshot; + const nextSnapshot = shouldIgnoreTokenDuringLogout ? toSignedOutSnapshot(snapshot) : snapshot; const previousIdentity = snapshotIdentity(previous.snapshot); const nextIdentity = snapshotIdentity(nextSnapshot); @@ -146,10 +147,6 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) previousIdentity !== nextIdentity || (previous.snapshot.auth.isAuthenticated && !nextSnapshot.auth.isAuthenticated); - if (shouldSuppressTokenRehydrate) { - log('[logout] suppressed stale token during post-logout guard window'); - } - return { ...previous, isBootstrapping: false, @@ -300,10 +297,8 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) return; } - // Invalidate stale in-flight snapshot refreshes that started pre-login. snapshotRequestIdRef.current += 1; logoutGuardUntilRef.current = 0; - log('[deep-link-auth] optimistic session token sync from deep-link event'); memoryTokenRef.current = token; commitState(previous => ({ @@ -394,35 +389,17 @@ export default function CoreStateProvider({ children }: { children: ReactNode }) ); const clearSession = useCallback(async () => { - // Ignore stale auth snapshots for a short window immediately after logout - // to prevent token "resurrection" flicker in route guards. logoutGuardUntilRef.current = Date.now() + 5_000; - - // Bump the snapshot request counter before doing anything else so that - // any snapshot poll already in flight when the user clicked logout is - // invalidated on return — otherwise its stale "authenticated" result - // would clobber our cleared state a few hundred ms after logout. snapshotRequestIdRef.current += 1; - // Optimistic local clear for instant UI response. commitState(previous => ({ ...previous, teams: [], teamMembersById: {}, teamInvitesById: {}, - snapshot: { - ...previous.snapshot, - auth: { isAuthenticated: false, userId: null, user: null, profileId: null }, - sessionToken: null, - currentUser: null, - onboardingCompleted: false, - }, + snapshot: toSignedOutSnapshot(previous.snapshot), })); memoryTokenRef.current = null; await tauriLogout(); - // Re-pull the authoritative snapshot from the core so the frontend - // cache matches whatever the core now reports. This mirrors the pattern - // used by storeSessionToken and ensures any downstream consumer reading - // from the snapshot sees the post-logout state immediately. await refresh().catch(err => { log('refresh failed after clearSession: %O', sanitizeError(err)); }); diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index b9c8211e8c..a7f800a9b5 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -14,6 +14,8 @@ import { evaluateOAuthAppVersionGate } from './oauthAppVersionGate'; import { openUrl } from './openUrl'; import { storeSession } from './tauriCommands'; +const SESSION_TOKEN_UPDATED_EVENT = 'core-state:session-token-updated'; + const focusMainWindow = async () => { try { const window = getCurrentWindow(); @@ -41,6 +43,16 @@ const waitForAuthReadiness = async (maxAttempts = 10, delayMs = 150) => { console.warn('[DeepLink][auth] readiness timeout; continuing'); }; +const applySessionToken = async (sessionToken: string): Promise => { + await storeSession(sessionToken, {}); + patchCoreStateSnapshot({ snapshot: { sessionToken } }); + window.dispatchEvent( + new CustomEvent(SESSION_TOKEN_UPDATED_EVENT, { + detail: { sessionToken }, + }) + ); +}; + /** * Handle an `openhuman://auth?token=...` deep link for login. */ @@ -55,31 +67,12 @@ const handleAuthDeepLink = async (parsed: URL) => { beginDeepLinkAuthProcessing(); - console.log('[DeepLink][auth] received', { - tokenLength: token.length, - keyMode: parsed.searchParams.get('key') ?? 'consume', - }); - try { await focusMainWindow(); await waitForAuthReadiness(); - if (key === 'auth') { - await storeSession(token, {}); - patchCoreStateSnapshot({ snapshot: { sessionToken: token } }); - window.dispatchEvent( - new CustomEvent('core-state:session-token-updated', { detail: { sessionToken: token } }) - ); - console.log('[DeepLink][auth] bypass token applied'); - } else { - const jwtToken = await consumeLoginToken(token); - await storeSession(jwtToken, {}); - patchCoreStateSnapshot({ snapshot: { sessionToken: jwtToken } }); - window.dispatchEvent( - new CustomEvent('core-state:session-token-updated', { detail: { sessionToken: jwtToken } }) - ); - console.log('[DeepLink][auth] login token consumed'); - } + const sessionToken = key === 'auth' ? token : await consumeLoginToken(token); + await applySessionToken(sessionToken); window.location.hash = '/home'; completeDeepLinkAuthProcessing(); From d086d39bc6a775a8c4f55c248f0fea55c1fbd571 Mon Sep 17 00:00:00 2001 From: shanu Date: Thu, 16 Apr 2026 18:35:36 +0530 Subject: [PATCH 4/5] chore(dependencies): update OpenHuman to version 0.52.16 in Cargo.lock files --- Cargo.lock | 2 +- app/src-tauri/Cargo.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96bd1e7513..304e142b58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4307,7 +4307,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openhuman" -version = "0.52.15" +version = "0.52.16" dependencies = [ "aes-gcm", "anyhow", diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 4411635306..aa1c9d649b 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "OpenHuman" -version = "0.52.14" +version = "0.52.16" dependencies = [ "env_logger", "log", From 525938607f519b7eda12d359e7aa06f15e8194fb Mon Sep 17 00:00:00 2001 From: shanu Date: Thu, 16 Apr 2026 18:39:22 +0530 Subject: [PATCH 5/5] refactor(format): format the code --- app/src/utils/desktopDeepLinkListener.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/utils/desktopDeepLinkListener.ts b/app/src/utils/desktopDeepLinkListener.ts index a7f800a9b5..88fa878813 100644 --- a/app/src/utils/desktopDeepLinkListener.ts +++ b/app/src/utils/desktopDeepLinkListener.ts @@ -46,11 +46,7 @@ const waitForAuthReadiness = async (maxAttempts = 10, delayMs = 150) => { const applySessionToken = async (sessionToken: string): Promise => { await storeSession(sessionToken, {}); patchCoreStateSnapshot({ snapshot: { sessionToken } }); - window.dispatchEvent( - new CustomEvent(SESSION_TOKEN_UPDATED_EVENT, { - detail: { sessionToken }, - }) - ); + window.dispatchEvent(new CustomEvent(SESSION_TOKEN_UPDATED_EVENT, { detail: { sessionToken } })); }; /**