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

3.3 KiB

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

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