SocialShare / frontend /src /contexts /AuthContext.jsx
NitinBot002's picture
Initial commit with all project files
f4854a1
import { createContext, useContext, useState, useEffect, useRef } from 'react';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
updateProfile,
sendPasswordResetEmail,
GoogleAuthProvider,
signInWithPopup,
signInWithRedirect,
getRedirectResult,
sendEmailVerification,
} from 'firebase/auth';
import { doc, setDoc, getDoc, serverTimestamp } from 'firebase/firestore';
import { auth, db } from '../services/firebase';
import { COLLECTIONS, ROLES } from '../utils/constants';
const AuthContext = createContext(null);
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [userProfile, setUserProfile] = useState(null);
const [loading, setLoading] = useState(true);
// Fetch user profile from Firestore
async function fetchUserProfile(user) {
if (!user) {
setUserProfile(null);
return null;
}
try {
const docRef = doc(db, COLLECTIONS.USERS, user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const profile = { uid: user.uid, ...docSnap.data() };
setUserProfile(profile);
return profile;
}
return null;
} catch (error) {
console.error('Error fetching user profile:', error);
return null;
}
}
// Register with email/password
async function register(email, password, displayName, phoneNumber = '') {
const { user } = await createUserWithEmailAndPassword(auth, email, password);
// Update Firebase Auth profile
await updateProfile(user, { displayName });
// Create Firestore user document
const userDoc = {
email,
displayName,
phoneNumber,
role: ROLES.CITIZEN,
createdAt: serverTimestamp(),
isWhitelisted: false,
profileImage: '',
status: 'active',
};
await setDoc(doc(db, COLLECTIONS.USERS, user.uid), userDoc);
setUserProfile({ uid: user.uid, ...userDoc });
// Send verification email
await sendEmailVerification(user);
return user;
}
// Login with email/password
async function login(email, password) {
const { user } = await signInWithEmailAndPassword(auth, email, password);
await fetchUserProfile(user);
return user;
}
// Create or fetch Google user profile in Firestore
async function handleGoogleUser(user) {
const docRef = doc(db, COLLECTIONS.USERS, user.uid);
const docSnap = await getDoc(docRef);
if (!docSnap.exists()) {
// First time Google sign-in — create profile
const userDoc = {
email: user.email,
displayName: user.displayName || '',
phoneNumber: user.phoneNumber || '',
role: ROLES.CITIZEN,
createdAt: serverTimestamp(),
isWhitelisted: false,
profileImage: user.photoURL || '',
status: 'active',
};
await setDoc(docRef, userDoc);
setUserProfile({ uid: user.uid, ...userDoc });
} else {
await fetchUserProfile(user);
}
return user;
}
// Google sign-in — try popup first, fall back to redirect
async function loginWithGoogle() {
const provider = new GoogleAuthProvider();
try {
const { user } = await signInWithPopup(auth, provider);
return await handleGoogleUser(user);
} catch (error) {
// If popup blocked by COOP or browser policy, use redirect
if (
error.code === 'auth/popup-blocked' ||
error.code === 'auth/popup-closed-by-user' ||
error.code === 'auth/cancelled-popup-request' ||
error.message?.includes('Cross-Origin-Opener-Policy')
) {
await signInWithRedirect(auth, provider);
return null; // Will be handled by getRedirectResult on reload
}
throw error;
}
}
// Logout
async function logout() {
await signOut(auth);
setCurrentUser(null);
setUserProfile(null);
}
// Reset password
async function resetPassword(email) {
await sendPasswordResetEmail(auth, email);
}
// Resend Verification Email
async function resendVerification() {
if (currentUser) {
await sendEmailVerification(currentUser);
}
}
// Check role access
function hasRole(requiredRole) {
if (!userProfile) return false;
const roleHierarchy = [
ROLES.CITIZEN,
ROLES.VOLUNTEER_COORDINATOR,
ROLES.MANAGER,
ROLES.CONTENT_MANAGER,
ROLES.ADMIN,
];
const userRoleIndex = roleHierarchy.indexOf(userProfile.role);
const requiredRoleIndex = roleHierarchy.indexOf(requiredRole);
return userRoleIndex >= requiredRoleIndex;
}
// Check specific role (exact match)
function isRole(role) {
return userProfile?.role === role;
}
// Handle redirect result (for Google sign-in redirect fallback)
const redirectHandled = useRef(false);
useEffect(() => {
if (redirectHandled.current) return;
redirectHandled.current = true;
getRedirectResult(auth)
.then(async (result) => {
if (result?.user) {
await handleGoogleUser(result.user);
}
})
.catch((error) => {
console.error('Redirect result error:', error);
});
}, []);
// Auth state listener
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (user) => {
setCurrentUser(user);
if (user) {
await fetchUserProfile(user);
} else {
setUserProfile(null);
}
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
userProfile,
loading,
register,
login,
loginWithGoogle,
logout,
resetPassword,
resendVerification,
hasRole,
isRole,
fetchUserProfile,
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
export default AuthContext;