Skip to content

fix(websocket): enable WebSocket authentication via query parameter#60

Merged
JoshuaAFerguson merged 6 commits into
mainfrom
claude/fix-realtime-connection-01T8b2a4D4SJvA23CnFteVfV
Nov 17, 2025
Merged

fix(websocket): enable WebSocket authentication via query parameter#60
JoshuaAFerguson merged 6 commits into
mainfrom
claude/fix-realtime-connection-01T8b2a4D4SJvA23CnFteVfV

Conversation

@JoshuaAFerguson

Copy link
Copy Markdown
Member

PROBLEM:

  • WebSocket connections were failing with authentication errors
  • Frontend could not establish real-time connections
  • Users saw "real-time connection error" and lost live updates

ROOT CAUSE:

  • Browser WebSocket API cannot send custom HTTP headers (e.g., Authorization)
  • Auth middleware only checked Authorization header, which wasn't available
  • Frontend had token but no way to send it during WebSocket handshake

SOLUTION:
Backend (api/internal/auth/middleware.go):

  • Modified auth middleware to accept token from query parameter for WebSocket connections
  • Maintains backward compatibility with Authorization header for regular HTTP requests
  • Checks query parameter first for WebSocket upgrades, falls back to header

Frontend (ui/src/hooks/useEnterpriseWebSocket.ts):

  • Updated WebSocket URL to include token as query parameter
  • Removed redundant token check (now handled in URL construction)
  • Uses encodeURIComponent for proper URL encoding

SECURITY:

  • Query parameter auth only used for WebSocket upgrade requests
  • Token still validated with same JWT verification process
  • No change to security model, just different transport mechanism

TESTING:

  • WebSocket connections should now authenticate successfully
  • Real-time updates should work (session status, notifications, etc.)
  • Frontend should show "Real-time updates connected" notification

Fixes: WebSocket authentication and real-time connection issues

PROBLEM:
- WebSocket connections were failing with authentication errors
- Frontend could not establish real-time connections
- Users saw "real-time connection error" and lost live updates

ROOT CAUSE:
- Browser WebSocket API cannot send custom HTTP headers (e.g., Authorization)
- Auth middleware only checked Authorization header, which wasn't available
- Frontend had token but no way to send it during WebSocket handshake

SOLUTION:
Backend (api/internal/auth/middleware.go):
- Modified auth middleware to accept token from query parameter for WebSocket connections
- Maintains backward compatibility with Authorization header for regular HTTP requests
- Checks query parameter first for WebSocket upgrades, falls back to header

Frontend (ui/src/hooks/useEnterpriseWebSocket.ts):
- Updated WebSocket URL to include token as query parameter
- Removed redundant token check (now handled in URL construction)
- Uses encodeURIComponent for proper URL encoding

SECURITY:
- Query parameter auth only used for WebSocket upgrade requests
- Token still validated with same JWT verification process
- No change to security model, just different transport mechanism

TESTING:
- WebSocket connections should now authenticate successfully
- Real-time updates should work (session status, notifications, etc.)
- Frontend should show "Real-time updates connected" notification

Fixes: WebSocket authentication and real-time connection issues
…WebSocket error dialog

- Changed primary button from 'Reload Page' to 'Continue Without Live Updates'
- Moved 'Reload Page' to secondary button position
- Makes it easier to continue using the app when WebSocket is unavailable
- Removes confusing suggestion to try refreshing
- Added 'ws: true' to Vite proxy config for /api route
- Fixes NS_ERROR_CONNECTION_REFUSED for WebSocket connections
- WebSocket connections were failing because Vite wasn't upgrading HTTP to WS

Without this option, Vite proxy blocks WebSocket upgrade requests,
causing all WebSocket connections to fail with CONNECTION_REFUSED.
…uthentication

PROBLEM:
- WebSocket connections for /ws/sessions and /ws/cluster were failing
- Connections were trying to reach ws://localhost:8000 directly
- Should connect through Vite proxy at ws://localhost:3000
- All WebSocket endpoints require authentication

CHANGES:
1. Updated useSessionsWebSocket to use window.location.host (not hardcoded)
2. Updated useMetricsWebSocket to use window.location.host
3. Updated useLogsWebSocket to use window.location.host
4. Added token authentication via query parameter to all three hooks

This ensures:
- Development: Connects to ws://localhost:3000 → Vite proxy → localhost:8000
- Production: Connects to wss://yourdomain.com directly
- All connections authenticated with JWT token from localStorage

Previously these hooks used VITE_API_URL environment variable which defaulted
to http://localhost:8000, bypassing the Vite proxy entirely.
- Added 'dismissed' state to track if user has dismissed the error
- After dismissal, subsequent WebSocket errors are logged to console only
- Prevents full-screen error dialog from taking over the screen repeatedly
- User can continue using the app without constant interruptions
…sive

- Moved reconnection banner from top-center to bottom-left
- Changed from warning (filled) to info (outlined) style
- Added dismiss button to permanently hide the banner
- Made it less visually aggressive so it doesn't block the UI
- Users can now continue using the app while WebSocket reconnects
@JoshuaAFerguson JoshuaAFerguson merged commit f040ab0 into main Nov 17, 2025
8 of 23 checks passed
@JoshuaAFerguson JoshuaAFerguson deleted the claude/fix-realtime-connection-01T8b2a4D4SJvA23CnFteVfV branch November 17, 2025 15:35
JoshuaAFerguson pushed a commit that referenced this pull request Nov 17, 2025
The WebSocket reconnection dialog was causing an infinite reconnection
loop that made the UI unusable. This was happening because:

1. WebSocket callback functions were being recreated on every render
2. When callbacks changed, the WebSocket hooks saw them as new dependencies
3. This triggered WebSocket disconnection and reconnection
4. State updates from reconnection triggered component re-renders
5. Back to step 1, creating an infinite loop

Fixed by:
- Using useRef to store WebSocket callbacks in both useWebSocket and
  useEnterpriseWebSocket hooks
- Updating refs when callbacks change instead of recreating connections
- Wrapping callbacks in useCallback in Dashboard, SessionViewer, and
  SharedSessions pages (defense in depth)
- Removing callback dependencies from WebSocket connect() functions

This ensures that callback changes don't trigger reconnection, breaking
the loop and making the UI stable again.

Fixes reconnection issues from PRs #60 and #61.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants