From a6e4981f17d68b575a394d6ad4ee041cfaeb1cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Gierwia=C5=82o?= Date: Fri, 14 Nov 2025 14:36:49 +0100 Subject: [PATCH] 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. --- backend/src/socket/index.js | 23 ++++----- frontend/src/pages/EventChatPage.jsx | 71 ++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/backend/src/socket/index.js b/backend/src/socket/index.js index cef1966..9713dbc 100644 --- a/backend/src/socket/index.js +++ b/backend/src/socket/index.js @@ -69,26 +69,27 @@ function initializeSocket(httpServer) { const eventId = event.id; const roomName = `event_${eventId}`; - socket.join(roomName); - socket.currentEventRoom = roomName; - socket.currentEventId = eventId; - socket.currentEventSlug = slug; - // Record event participation in database - await prisma.eventParticipant.upsert({ + // Verify that user has checked in to this event + const participant = await prisma.eventParticipant.findUnique({ where: { userId_eventId: { userId: socket.user.id, eventId: eventId, }, }, - update: {}, // Don't update anything if already exists - create: { - userId: socket.user.id, - eventId: eventId, - }, }); + if (!participant) { + socket.emit('error', { message: 'You must check in to this event first' }); + return; + } + + socket.join(roomName); + socket.currentEventRoom = roomName; + socket.currentEventId = eventId; + socket.currentEventSlug = slug; + // Add user to active users if (!activeUsers.has(eventId)) { activeUsers.set(eventId, new Set()); diff --git a/frontend/src/pages/EventChatPage.jsx b/frontend/src/pages/EventChatPage.jsx index 3cba8da..49d09ae 100644 --- a/frontend/src/pages/EventChatPage.jsx +++ b/frontend/src/pages/EventChatPage.jsx @@ -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 ( + +
+
+
+
+ +
+

+ Check-in Required +

+

+ You need to check in at the event venue by scanning the QR code to access this chat. +

+ +
+

+ {event.name} +

+

+ {event.location} +

+
+ +
+ + Back to Events + + {import.meta.env.DEV && ( + + View QR Code (dev) + + )} +
+
+
+
+
+ ); + } + return (