- Changed cookie SameSite policy from Lax to None with Secure=true for Safari ITP compatibility - Fixed Nuxt composable context issue: moved useRuntimeConfig() from connect() callback to composable setup phase (required in Nuxt 4) - Added GET /logout endpoint for easy browser-based logout - Improved loading overlay with clear status indicators and action buttons (Retry, Re-Login, Dismiss) - Added error handling with try-catch in WebSocket connect() - Documented issue and fixes in .claude/SAFARI_WEBSOCKET_ISSUE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
66 lines
2.5 KiB
Markdown
66 lines
2.5 KiB
Markdown
# 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. 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
|
|
|
|
## 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 |
|