1- import { createContext , useContext , useEffect , useState , ReactNode } from 'react' ;
2- import {
3- User ,
4- createUserWithEmailAndPassword ,
5- signInWithEmailAndPassword ,
6- signOut ,
7- onAuthStateChanged ,
8- sendPasswordResetEmail ,
9- signInWithPopup ,
10- updateProfile ,
11- GoogleAuthProvider ,
12- Auth
13- } from 'firebase/auth' ;
14- import { auth as firebaseAuth , googleProvider as firebaseGoogleProvider } from '@/lib/firebase' ;
15- import { useToast } from '@/hooks/use-toast' ;
16- import { apiRequest } from '@/lib/queryClient' ;
17-
18- // Type assertions to ensure TypeScript understands our imports from firebase.ts
19- const auth = firebaseAuth as Auth ;
20- const googleProvider = firebaseGoogleProvider as GoogleAuthProvider ;
21-
22- interface AuthContextProps {
23- currentUser : User | null ;
24- isLoading : boolean ;
25- signUp : ( email : string , password : string ) => Promise < User > ;
26- login : ( email : string , password : string ) => Promise < User > ;
27- logout : ( ) => Promise < void > ;
28- resetPassword : ( email : string ) => Promise < void > ;
29- signInWithGoogle : ( ) => Promise < User > ;
30- updateUserProfile : ( displayName : string ) => Promise < void > ;
31- }
32-
33- const AuthContext = createContext < AuthContextProps | null > ( null ) ;
34-
35- export function useAuth ( ) {
36- const context = useContext ( AuthContext ) ;
37- if ( ! context ) {
38- throw new Error ( 'useAuth must be used within an AuthProvider' ) ;
39- }
40- return context ;
1+ // client/src/contexts/AuthContext.tsx
2+
3+ import React , { createContext , ReactNode , useContext } from "react" ;
4+ import { useAuth } from "@/lib/useAuth" ;
5+
6+ interface AppUser {
7+ id : string ;
8+ email : string | null ;
9+ displayName : string | null ;
10+ photoURL : string | null ;
11+ createdAt : string ;
12+ updatedAt : string ;
4113}
4214
43- interface AuthProviderProps {
44- children : ReactNode ;
15+ interface AuthContextType {
16+ user : AppUser | null ;
17+ loading : boolean ;
18+ signIn : ( ) => Promise < void > ;
4519}
4620
47- export function AuthProvider ( { children } : AuthProviderProps ) {
48- const [ currentUser , setCurrentUser ] = useState < User | null > ( null ) ;
49- const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
50- const { toast } = useToast ( ) ;
51-
52- // Function to register user with our backend
53- async function registerUserWithBackend ( user : User ) {
54- try {
55- if ( user ) {
56- await fetch ( '/api/auth/user' , {
57- method : 'POST' ,
58- headers : {
59- 'Content-Type' : 'application/json' ,
60- } ,
61- body : JSON . stringify ( {
62- uid : user . uid ,
63- email : user . email ,
64- displayName : user . displayName ,
65- photoURL : user . photoURL
66- } )
67- } ) ;
68- }
69- } catch ( error ) {
70- console . error ( "Error syncing user with backend:" , error ) ;
71- }
72- }
73-
74- useEffect ( ( ) => {
75- const unsubscribe = onAuthStateChanged ( auth , ( user ) => {
76- setCurrentUser ( user ) ;
77-
78- // Register the user with our backend when they authenticate
79- if ( user ) {
80- registerUserWithBackend ( user )
81- . catch ( error => console . error ( "Failed to register user with backend:" , error ) ) ;
82- }
83-
84- setIsLoading ( false ) ;
85- } ) ;
86-
87- return unsubscribe ;
88- } , [ ] ) ;
89-
90- async function signUp ( email : string , password : string ) : Promise < User > {
91- try {
92- const userCredential = await createUserWithEmailAndPassword ( auth , email , password ) ;
93- // Register with our backend
94- await registerUserWithBackend ( userCredential . user ) ;
95- return userCredential . user ;
96- } catch ( error : any ) {
97- const errorMessage = error . message || 'An error occurred during sign up' ;
98- toast ( {
99- title : 'Sign up failed' ,
100- description : errorMessage ,
101- variant : 'destructive'
102- } ) ;
103- throw error ;
104- }
105- }
106-
107- async function login ( email : string , password : string ) : Promise < User > {
108- try {
109- const userCredential = await signInWithEmailAndPassword ( auth , email , password ) ;
110- // Register with our backend
111- await registerUserWithBackend ( userCredential . user ) ;
112- return userCredential . user ;
113- } catch ( error : any ) {
114- const errorMessage = error . message || 'An error occurred during login' ;
115- toast ( {
116- title : 'Login failed' ,
117- description : errorMessage ,
118- variant : 'destructive'
119- } ) ;
120- throw error ;
121- }
122- }
123-
124- async function logout ( ) : Promise < void > {
125- try {
126- await signOut ( auth ) ;
127- // No need to update backend as we're checking auth state in our API
128- } catch ( error : any ) {
129- const errorMessage = error . message || 'An error occurred during logout' ;
130- toast ( {
131- title : 'Logout failed' ,
132- description : errorMessage ,
133- variant : 'destructive'
134- } ) ;
135- throw error ;
136- }
137- }
138-
139- async function resetPassword ( email : string ) : Promise < void > {
140- try {
141- await sendPasswordResetEmail ( auth , email ) ;
142- toast ( {
143- title : 'Password reset email sent' ,
144- description : 'Check your email for further instructions' ,
145- } ) ;
146- } catch ( error : any ) {
147- const errorMessage = error . message || 'An error occurred during password reset' ;
148- toast ( {
149- title : 'Password reset failed' ,
150- description : errorMessage ,
151- variant : 'destructive'
152- } ) ;
153- throw error ;
154- }
155- }
156-
157- async function signInWithGoogle ( ) : Promise < User > {
158- try {
159- // Ensure Firebase auth is available
160- if ( ! auth ) {
161- throw new Error ( "Firebase authentication is not available" ) ;
162- }
163-
164- // Create a new GoogleAuthProvider instance for this sign-in attempt
165- // This ensures we have a fresh provider each time
166- const provider = new GoogleAuthProvider ( ) ;
167-
168- // Add the scopes we need
169- provider . addScope ( 'email' ) ;
170- provider . addScope ( 'profile' ) ;
171-
172- // Set custom parameters
173- provider . setCustomParameters ( {
174- prompt : 'select_account'
175- } ) ;
176-
177- // Use popup for mobile compatibility
178- const result = await signInWithPopup ( auth , provider ) ;
179-
180- // Register with our backend
181- await registerUserWithBackend ( result . user ) ;
182- return result . user ;
183- } catch ( error : any ) {
184- // Provide a more user-friendly error message
185- let errorMessage = 'An error occurred during Google sign in' ;
186-
187- if ( error . code === 'auth/popup-blocked' ) {
188- errorMessage = 'The sign-in popup was blocked by your browser. Please allow popups for this site.' ;
189- } else if ( error . code === 'auth/popup-closed-by-user' ) {
190- errorMessage = 'The sign-in popup was closed before completing the process.' ;
191- } else if ( error . code === 'auth/cancelled-popup-request' ) {
192- errorMessage = 'The operation was cancelled.' ;
193- } else if ( error . code === 'auth/unauthorized-domain' ) {
194- errorMessage = 'This domain is not authorized for Google sign-in. Please add this domain to your Firebase console under Authentication > Settings > Authorized domains.' ;
195- } else if ( error . code === 'auth/configuration-not-found' ) {
196- errorMessage = 'Firebase configuration error. Please try email/password login or contact support.' ;
197- } else if ( error . message ) {
198- errorMessage = error . message ;
199- }
200-
201- toast ( {
202- title : 'Google sign in failed' ,
203- description : errorMessage ,
204- variant : 'destructive'
205- } ) ;
206- throw error ;
207- }
208- }
209-
210- async function updateUserProfile ( displayName : string ) : Promise < void > {
211- try {
212- if ( currentUser ) {
213- await updateProfile ( currentUser , { displayName } ) ;
214- // Force refresh the user object
215- setCurrentUser ( { ...currentUser } ) ;
216-
217- // Update user profile in our backend
218- await registerUserWithBackend ( currentUser ) ;
219- }
220- } catch ( error : any ) {
221- const errorMessage = error . message || 'An error occurred while updating profile' ;
222- toast ( {
223- title : 'Profile update failed' ,
224- description : errorMessage ,
225- variant : 'destructive'
226- } ) ;
227- throw error ;
228- }
229- }
230-
231- const value = {
232- currentUser,
233- isLoading,
234- signUp,
235- login,
236- logout,
237- resetPassword,
238- signInWithGoogle,
239- updateUserProfile
240- } ;
21+ const AuthContext = createContext < AuthContextType | undefined > ( undefined ) ;
24122
23+ export function AuthProvider ( { children } : { children : ReactNode } ) {
24+ const { user, loading, signIn } = useAuth ( ) ;
24225 return (
243- < AuthContext . Provider value = { value } >
26+ < AuthContext . Provider value = { { user , loading , signIn } } >
24427 { children }
24528 </ AuthContext . Provider >
24629 ) ;
247- }
30+ }
31+
32+ export function useAuthContext ( ) {
33+ const ctx = useContext ( AuthContext ) ;
34+ if ( ! ctx ) throw new Error ( "useAuthContext must be used within AuthProvider" ) ;
35+ return ctx ;
36+ }
0 commit comments