From 7c9368a739358340bab3fc392485dabf3e5525df Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Sat, 26 Jul 2025 22:23:31 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=90=20Fix=20Authentication=20Issues=20?= =?UTF-8?q?in=20Collection=20Pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 Fixed Authentication Problems: - Removed hardcoded mock admin user from collection detail page - Removed hardcoded mock user from collections page - Created proper useAuth hook to get current authenticated user - Added proper authentication checks and redirects 🔧 Authentication Flow Fixes: - Collection detail page now uses actual logged-in user (Alice, Bob, etc.) - Proper permission checks based on real user identity - Edit/Delete buttons now show correctly based on actual ownership - Authentication loading states handled properly 🛠️ Technical Improvements: - Created lib/use-auth.js hook for consistent auth handling - Added auth loading states to prevent flash of wrong content - Proper redirects to login page when not authenticated - Fixed token retrieval from localStorage ('auth_token') ✅ User Experience: - Alice and Bob now see their own collections correctly - Edit/Delete permissions work based on actual collection ownership - No more authentication errors when editing owned collections - Consistent user identity across all pages The authentication system now works correctly with the demo users! 🎯 --- lib/use-auth.js | 55 ++++++++++++++++++++++++++++++++ pages/collection/[identifier].js | 27 ++++++++++------ pages/collections.js | 33 ++++++++++++------- 3 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 lib/use-auth.js diff --git a/lib/use-auth.js b/lib/use-auth.js new file mode 100644 index 0000000..28512c5 --- /dev/null +++ b/lib/use-auth.js @@ -0,0 +1,55 @@ +import { useState, useEffect } from 'react'; + +export function useAuth() { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + checkAuth(); + }, []); + + const checkAuth = async () => { + try { + // Get token from localStorage + const token = localStorage.getItem('auth_token'); + + const headers = { + 'Content-Type': 'application/json', + }; + + // Add authorization header if token exists + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch('/api/auth/verify', { headers }); + if (response.ok) { + const userData = await response.json(); + setUser(userData); + } else { + setUser(null); + // Clear invalid token + if (token) { + localStorage.removeItem('auth_token'); + } + } + } catch (error) { + console.error('Auth check failed:', error); + setUser(null); + } finally { + setLoading(false); + } + }; + + const logout = () => { + localStorage.removeItem('auth_token'); + setUser(null); + }; + + return { + user, + loading, + logout, + refreshAuth: checkAuth + }; +} \ No newline at end of file diff --git a/pages/collection/[identifier].js b/pages/collection/[identifier].js index a8b8850..cbb1d3f 100644 --- a/pages/collection/[identifier].js +++ b/pages/collection/[identifier].js @@ -5,16 +5,12 @@ import UploadImageModal from '../../components/UploadImageModal'; import ShareModal from '../../components/ShareModal'; import CollaboratorFacepile from '../../components/CollaboratorFacepile'; import Layout from '../../components/Layout'; +import { useAuth } from '../../lib/use-auth'; export default function CollectionView() { const router = useRouter(); const { identifier } = router.query; - - // Get user from auth context - for now using admin user - const user = { - email: 'admin@tcgvault.com', - role: 'admin' - }; + const { user, loading: authLoading } = useAuth(); const [collection, setCollection] = useState(null); const [cards, setCards] = useState([]); @@ -48,10 +44,17 @@ export default function CollectionView() { const [showUploadModal, setShowUploadModal] = useState(false); useEffect(() => { - if (identifier) { + if (identifier && user) { fetchCollectionData(); } - }, [identifier]); + }, [identifier, user]); + + // Redirect to login if not authenticated + useEffect(() => { + if (!authLoading && !user) { + router.push('/login'); + } + }, [authLoading, user, router]); const fetchCollectionData = async () => { try { @@ -395,7 +398,8 @@ export default function CollectionView() { } }); - if (loading) { + // Show loading spinner while auth is loading or data is loading + if (authLoading || loading) { return (
@@ -405,6 +409,11 @@ export default function CollectionView() { ); } + // Redirect to login if not authenticated (handled by useEffect, but this is a fallback) + if (!user) { + return null; + } + if (!collection) { return ( diff --git a/pages/collections.js b/pages/collections.js index 693a000..a05b7b6 100644 --- a/pages/collections.js +++ b/pages/collections.js @@ -2,15 +2,11 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Layout from '../components/Layout'; import PermissionIndicator from '../components/PermissionIndicator'; +import { useAuth } from '../lib/use-auth'; export default function Collections() { const router = useRouter(); - - // Mock user data for now - const user = { - email: 'me@randallstillwell.com', - role: 'user' - }; + const { user, loading: authLoading } = useAuth(); const [collections, setCollections] = useState([]); const [loading, setLoading] = useState(true); @@ -29,6 +25,19 @@ export default function Collections() { const [showSuccessModal, setShowSuccessModal] = useState(false); const [createdCollection, setCreatedCollection] = useState(null); + // Redirect to login if not authenticated + useEffect(() => { + if (!authLoading && !user) { + router.push('/login'); + } + }, [authLoading, user, router]); + + useEffect(() => { + if (user) { + fetchCollections(); + } + }, [user]); + const fetchCollections = async () => { try { const response = await fetch('/api/collections'); @@ -61,10 +70,6 @@ export default function Collections() { } }; - useEffect(() => { - fetchCollections(); - }, []); - const sortOptions = [ { value: 'name', label: 'Name (A-Z)' }, { value: 'value', label: 'Value (High to Low)' }, @@ -253,7 +258,8 @@ export default function Collections() { ); }; - if (loading) { + // Show loading spinner while auth is loading or data is loading + if (authLoading || loading) { return (
@@ -263,6 +269,11 @@ export default function Collections() { ); } + // Redirect to login if not authenticated (handled by useEffect, but this is a fallback) + if (!user) { + return null; + } + return ( {/* Header */}