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