# Safari/iPad WebSocket Connection Issue ## Problem Summary WebSocket connections intermittently fail on iPad Safari while working reliably on desktop browsers. **Pattern:** - Works initially after login - Randomly fails (minutes to hours later) - "Fixes itself" during debugging (suggesting cache/timing issues) - Desktop browsers never have this problem ## Root Causes Identified ### 1. Cookie SameSite Policy (Fixed 2025-11-28) Safari's ITP treats `SameSite=Lax` differently than Chrome/Firefox. Safari may not send cookies with fetch/XHR even on same-origin. **Fix:** Changed to `SameSite=None; Secure=true` in `backend/app/utils/cookies.py` ### 2. Nuxt Composable Context Issue (Fixed 2025-11-28) `useRuntimeConfig()` was being called inside `connect()` function, which is invoked from: - `onMounted` hooks (valid context) - `setInterval` callbacks (INVALID context) In Nuxt 4, composables MUST be called during component setup, not inside callbacks. **Fix:** Moved `useRuntimeConfig()` to composable setup phase in `useWebSocket.ts` ### 3. Auth Middleware Disabled on Game Page (Fixed 2025-11-28) The game detail page (`pages/games/[id].vue`) had `middleware: ['auth']` commented out for WebSocket testing. This caused: - SSR rendered page without checking auth - Page showed "Auth: Failed" immediately from SSR - Safari's client-side auth check wasn't reaching the backend (likely cached) **Fix:** Re-enabled `middleware: ['auth']` in `definePageMeta` on the game page. ### 4. Potential Safari-Specific Issues (Not Yet Fixed) - **Safari ITP**: Can expire cookies unpredictably - **iOS Memory Management**: Safari may clear JS state when backgrounded - **Cached Old Code**: Safari may aggressively cache outdated JS bundles ## Files Modified 1. `backend/app/utils/cookies.py` - Cookie SameSite policy 2. `frontend-sba/composables/useWebSocket.ts` - Composable context fix 3. `backend/app/api/routes/auth.py` - Added GET /logout endpoint, added /ws-token endpoint 4. `frontend-sba/pages/games/[id].vue` - Re-enabled auth middleware 5. `backend/app/websocket/handlers.py` - Added token query param support for Safari fallback ## Prevention Strategies ### Short-term (Implemented) - Stuck state detector every 5 seconds - Multiple retry mechanisms in websocket.client.ts plugin - Error logging in connect() function ### Long-term (Consider Implementing) 1. **Token in WebSocket URL**: Pass short-lived auth token as query param (fallback if cookies fail) 2. **Connection health indicator**: Visible UI showing connection state, not just loading spinner 3. **Automatic logout/re-login flow**: If connection fails repeatedly, redirect to login ## Debugging Steps 1. Check backend logs: `tail -f logs/backend.log | grep -E "auth|Cookie|WebSocket"` 2. Check browser console for `[WebSocket]` prefixed logs 3. Verify cookies exist: Safari Settings > Advanced > Website Data > gameplay-demo.manticorum.com 4. Force cache clear: Close Safari completely, reopen ## History | Date | Issue | Fix | Result | |------|-------|-----|--------| | 2025-11-28 | SameSite=Lax not working on Safari | Changed to SameSite=None | Worked initially | | 2025-11-28 | useRuntimeConfig in callback | Moved to setup phase | Connected | | 2025-11-28 | Auth middleware disabled on game page | Re-enabled middleware: ['auth'] | Auth: OK, WebSocket connected |