🔐 Fix Authentication Issues in Collection Pages

🐛 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! 🎯
This commit is contained in:
Randall Stillwell 2025-07-26 22:23:31 -05:00
parent 374ad421f6
commit 7c9368a739
3 changed files with 95 additions and 20 deletions

55
lib/use-auth.js Normal file
View file

@ -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
};
}

View file

@ -5,16 +5,12 @@ import UploadImageModal from '../../components/UploadImageModal';
import ShareModal from '../../components/ShareModal'; import ShareModal from '../../components/ShareModal';
import CollaboratorFacepile from '../../components/CollaboratorFacepile'; import CollaboratorFacepile from '../../components/CollaboratorFacepile';
import Layout from '../../components/Layout'; import Layout from '../../components/Layout';
import { useAuth } from '../../lib/use-auth';
export default function CollectionView() { export default function CollectionView() {
const router = useRouter(); const router = useRouter();
const { identifier } = router.query; const { identifier } = router.query;
const { user, loading: authLoading } = useAuth();
// Get user from auth context - for now using admin user
const user = {
email: 'admin@tcgvault.com',
role: 'admin'
};
const [collection, setCollection] = useState(null); const [collection, setCollection] = useState(null);
const [cards, setCards] = useState([]); const [cards, setCards] = useState([]);
@ -48,10 +44,17 @@ export default function CollectionView() {
const [showUploadModal, setShowUploadModal] = useState(false); const [showUploadModal, setShowUploadModal] = useState(false);
useEffect(() => { useEffect(() => {
if (identifier) { if (identifier && user) {
fetchCollectionData(); fetchCollectionData();
} }
}, [identifier]); }, [identifier, user]);
// Redirect to login if not authenticated
useEffect(() => {
if (!authLoading && !user) {
router.push('/login');
}
}, [authLoading, user, router]);
const fetchCollectionData = async () => { const fetchCollectionData = async () => {
try { 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 ( return (
<Layout user={user}> <Layout user={user}>
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
@ -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) { if (!collection) {
return ( return (
<Layout user={user}> <Layout user={user}>

View file

@ -2,15 +2,11 @@ import { useState, useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Layout from '../components/Layout'; import Layout from '../components/Layout';
import PermissionIndicator from '../components/PermissionIndicator'; import PermissionIndicator from '../components/PermissionIndicator';
import { useAuth } from '../lib/use-auth';
export default function Collections() { export default function Collections() {
const router = useRouter(); const router = useRouter();
const { user, loading: authLoading } = useAuth();
// Mock user data for now
const user = {
email: 'me@randallstillwell.com',
role: 'user'
};
const [collections, setCollections] = useState([]); const [collections, setCollections] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -29,6 +25,19 @@ export default function Collections() {
const [showSuccessModal, setShowSuccessModal] = useState(false); const [showSuccessModal, setShowSuccessModal] = useState(false);
const [createdCollection, setCreatedCollection] = useState(null); 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 () => { const fetchCollections = async () => {
try { try {
const response = await fetch('/api/collections'); const response = await fetch('/api/collections');
@ -61,10 +70,6 @@ export default function Collections() {
} }
}; };
useEffect(() => {
fetchCollections();
}, []);
const sortOptions = [ const sortOptions = [
{ value: 'name', label: 'Name (A-Z)' }, { value: 'name', label: 'Name (A-Z)' },
{ value: 'value', label: 'Value (High to Low)' }, { 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 ( return (
<Layout user={user}> <Layout user={user}>
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
@ -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 ( return (
<Layout user={user}> <Layout user={user}>
{/* Header */} {/* Header */}