A social knowledge tool for researchers built on ATProto
at ff03e09bfaf3b3baf2f90cdc6562677f0331ff67 70 lines 2.1 kB view raw
1'use client'; 2 3import { createContext, useContext, ReactNode, useEffect } from 'react'; 4import { useQuery, useQueryClient } from '@tanstack/react-query'; 5import { useRouter } from 'next/navigation'; 6import type { GetProfileResponse } from '@/api-client/ApiClient'; 7import { ClientCookieAuthService } from '@/services/auth/CookieAuthService.client'; 8import { verifySessionOnClient } from '@/lib/auth/dal'; 9import { usePathname } from 'next/navigation'; 10 11interface AuthContextType { 12 user: GetProfileResponse | null; 13 isAuthenticated: boolean; 14 isLoading: boolean; 15 refreshAuth: () => Promise<void>; 16 logout: () => Promise<void>; 17} 18 19const AuthContext = createContext<AuthContextType | undefined>(undefined); 20 21export const AuthProvider = ({ children }: { children: ReactNode }) => { 22 const router = useRouter(); 23 const queryClient = useQueryClient(); 24 const pathname = usePathname(); // to prevent redirecting to login on landing page 25 26 const refreshAuth = async () => { 27 await query.refetch(); 28 }; 29 30 const logout = async () => { 31 await ClientCookieAuthService.clearTokens(); 32 queryClient.clear(); 33 router.push('/login'); 34 }; 35 36 const query = useQuery<GetProfileResponse | null>({ 37 queryKey: ['authenticated user'], 38 queryFn: async () => { 39 const session = await verifySessionOnClient(); 40 return session; 41 }, 42 staleTime: 5 * 60 * 1000, // cache for 5 minutes 43 refetchOnWindowFocus: false, 44 retry: false, 45 }); 46 47 useEffect(() => { 48 if (query.isError && !query.isLoading && pathname !== '/') logout(); 49 }, [query.isError, logout]); 50 51 const contextValue: AuthContextType = { 52 user: query.data ?? null, 53 isAuthenticated: !!query.data, 54 isLoading: query.isLoading, 55 refreshAuth, 56 logout, 57 }; 58 59 return ( 60 <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider> 61 ); 62}; 63 64export const useAuth = (): AuthContextType => { 65 const context = useContext(AuthContext); 66 if (!context) { 67 throw new Error('useAuth must be used within an AuthProvider'); 68 } 69 return context; 70};