🔐 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:
parent
374ad421f6
commit
7c9368a739
3 changed files with 95 additions and 20 deletions
55
lib/use-auth.js
Normal file
55
lib/use-auth.js
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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}>
|
||||||
|
|
|
||||||
|
|
@ -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 */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue