This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / hooks / useAuth.tsx
3.3 kB 111 lines
1'use client'; 2 3import { 4 createContext, 5 useCallback, 6 useContext, 7 useEffect, 8 useMemo, 9 ReactNode, 10} from 'react'; 11import { useQuery, useQueryClient } from '@tanstack/react-query'; 12import { useRouter } from 'next/navigation'; 13import type { GetProfileResponse } from '@/api-client/ApiClient'; 14import { ClientCookieAuthService } from '@/services/auth/CookieAuthService.client'; 15import { verifySessionOnClient } from '@/lib/auth/dal'; 16import { usePathname } from 'next/navigation'; 17import posthog from 'posthog-js'; 18import { isInternalUser, isEarlyTester } from '@/lib/userLists'; 19import { shouldCaptureAnalytics } from '@/features/analytics/utils'; 20import { ENABLE_AUTH_LOGGING } from '@/lib/auth/constants'; 21 22interface AuthContextType { 23 user: GetProfileResponse | null; 24 isAuthenticated: boolean; 25 isLoading: boolean; 26 refreshAuth: () => Promise<void>; 27 logout: () => Promise<void>; 28} 29 30const AuthContext = createContext<AuthContextType | undefined>(undefined); 31 32export const AuthProvider = ({ children }: { children: ReactNode }) => { 33 const router = useRouter(); 34 const queryClient = useQueryClient(); 35 const pathname = usePathname(); // to prevent redirecting to login on landing page 36 37 const logout = useCallback(async () => { 38 if (ENABLE_AUTH_LOGGING) { 39 console.log('[useAuth] Initiating logout process'); 40 } 41 42 // Reset PostHog user identity 43 if (shouldCaptureAnalytics()) { 44 posthog.reset(); 45 if (ENABLE_AUTH_LOGGING) { 46 console.log('[useAuth] PostHog user identity reset'); 47 } 48 } 49 50 await ClientCookieAuthService.clearTokens(); 51 queryClient.clear(); 52 router.push('/login'); 53 }, [queryClient, router]); 54 55 const query = useQuery<GetProfileResponse | null>({ 56 queryKey: ['authenticated user'], 57 queryFn: async () => { 58 const session = await verifySessionOnClient(); 59 return session; 60 }, 61 staleTime: 5 * 60 * 1000, // cache for 5 minutes 62 refetchOnWindowFocus: false, 63 retry: false, 64 }); 65 66 const refreshAuth = useCallback(async () => { 67 await query.refetch(); 68 }, [query.refetch]); 69 70 useEffect(() => { 71 // Handle other auth errors 72 if (query.isError && !query.isLoading && pathname !== '/') logout(); 73 }, [query.isError, query.isLoading, pathname, logout]); 74 75 // Set super properties for anonymous tracking (no PII) 76 useEffect(() => { 77 const user = query.data; 78 79 // Set super properties when analytics is enabled and user is available 80 // These properties are included in all events automatically 81 if (shouldCaptureAnalytics() && user) { 82 posthog.register({ 83 is_internal: isInternalUser(user.handle), 84 is_early_tester: isEarlyTester(user.handle), 85 }); 86 } 87 }, [query.data]); 88 89 const contextValue = useMemo<AuthContextType>( 90 () => ({ 91 user: query.data ?? null, 92 isAuthenticated: !!query.data, 93 isLoading: query.isLoading, 94 refreshAuth, 95 logout, 96 }), 97 [query.data, query.isLoading, refreshAuth, logout], 98 ); 99 100 return ( 101 <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider> 102 ); 103}; 104 105export const useAuth = (): AuthContextType => { 106 const context = useContext(AuthContext); 107 if (!context) { 108 throw new Error('useAuth must be used within an AuthProvider'); 109 } 110 return context; 111};