fix: prevent bypassing event check-in via page refresh

Users could gain unauthorized access to event chats by refreshing the page after leaving an event. The socket handler was automatically creating participation records when users joined rooms, completely bypassing the QR code check-in requirement. This fix verifies that users have legitimately checked in before allowing socket room access.
This commit is contained in:
Radosław Gierwiało
2025-11-14 14:36:49 +01:00
parent 6823851b63
commit a6e4981f17
2 changed files with 78 additions and 16 deletions

View File

@@ -1,8 +1,8 @@
import { useState, useRef, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useParams, useNavigate, Link } from 'react-router-dom';
import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext';
import { Send, UserPlus, Loader2, LogOut, AlertTriangle } from 'lucide-react';
import { Send, UserPlus, Loader2, LogOut, AlertTriangle, QrCode } from 'lucide-react';
import { connectSocket, disconnectSocket, getSocket } from '../services/socket';
import { eventsAPI } from '../services/api';
@@ -11,6 +11,7 @@ const EventChatPage = () => {
const { user } = useAuth();
const navigate = useNavigate();
const [event, setEvent] = useState(null);
const [isParticipant, setIsParticipant] = useState(false);
const [loading, setLoading] = useState(true);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
@@ -27,16 +28,27 @@ const EventChatPage = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
// Fetch event data
// Fetch event data and check participation
useEffect(() => {
const fetchEvent = async () => {
try {
setLoading(true);
const data = await eventsAPI.getBySlug(slug);
setEvent(data);
// Get all events with participation info
const allEvents = await eventsAPI.getAll();
const eventData = allEvents.find(e => e.slug === slug);
if (eventData) {
setEvent(eventData);
setIsParticipant(eventData.isJoined);
} else {
setEvent(null);
setIsParticipant(false);
}
} catch (err) {
console.error('Error loading event:', err);
setEvent(null);
setIsParticipant(false);
} finally {
setLoading(false);
}
@@ -237,6 +249,55 @@ const EventChatPage = () => {
);
}
// Check if user is participant
if (!isParticipant) {
return (
<Layout>
<div className="max-w-4xl mx-auto">
<div className="bg-white rounded-lg shadow-md p-8">
<div className="text-center">
<div className="w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4">
<QrCode className="w-8 h-8 text-amber-600" />
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">
Check-in Required
</h2>
<p className="text-gray-600 mb-6">
You need to check in at the event venue by scanning the QR code to access this chat.
</p>
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 mb-6">
<p className="text-sm text-amber-800 font-medium mb-1">
{event.name}
</p>
<p className="text-sm text-amber-700">
{event.location}
</p>
</div>
<div className="flex gap-3 justify-center">
<Link
to="/events"
className="px-6 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
Back to Events
</Link>
{import.meta.env.DEV && (
<Link
to={`/events/${slug}/details`}
className="px-6 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors"
>
View QR Code (dev)
</Link>
)}
</div>
</div>
</div>
</div>
</Layout>
);
}
return (
<Layout>
<div className="max-w-6xl mx-auto">