strat-gameplay-webapp/.claude/SAFARI_WEBSOCKET_ISSUE.md
Cal Corum acd080b437 CLAUDE: Fix Safari/iPad auth failure on game detail page
Root cause: Auth middleware was commented out on game detail page
([id].vue), causing SSR to render without checking authentication.
Safari's client-side auth check wasn't reaching the backend due to
caching behavior, resulting in "Auth: Failed" display.

Changes:
- Re-enabled middleware: ['auth'] in pages/games/[id].vue
- Added /api/auth/ws-token endpoint for Safari WebSocket fallback
- Added expires_minutes param to create_token() for short-lived tokens
- Added token query param support to WebSocket handlers
- Updated SAFARI_WEBSOCKET_ISSUE.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:53:20 -06:00

77 lines
3.3 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. 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 |