Skip to content

[MEDIUM] WebSocket frontend/backend authentication protocol mismatch #110

Description

@eovidiu

Summary

The frontend WebSocket client sends a session: 'cookie-auth' marker instead of the actual JWT token during WebSocket authentication. However, the backend WebSocket handler expects an actual JWT token in the token field. This mismatch causes WebSocket authentication to fail.

Vulnerability Details

  • CWE: CWE-287 (Improper Authentication)
  • CVSS Score: 6.5
  • Files:
    • Frontend: src/services/api.ts (line ~210)
    • Backend: backend/app/routers/websocket.py (line ~161-168)

Current Behavior

Frontend (src/services/api.ts):

this.ws?.send(JSON.stringify({
  action: 'authenticate',
  // Don't send actual token - server will validate via cookie on the HTTP upgrade
  session: 'cookie-auth'
}));

Backend (backend/app/routers/websocket.py):

token = auth_message.get("token")  # Expects 'token' field
if not token:
    await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
    return

payload = verify_websocket_token(token)  # Validates JWT

Problem

  • Frontend sends {action: 'authenticate', session: 'cookie-auth'}
  • Backend looks for auth_message.get("token") which is None
  • Backend closes connection due to missing token
  • WebSocket real-time features don't work

Remediation Options

Option A: Backend accepts cookie-based auth (Recommended)

Modify backend/app/routers/websocket.py to check for HttpOnly cookie if no token in message:

token = auth_message.get("token")

# If no token in message, try to get from cookie (for cookie-auth mode)
if not token and auth_message.get("session") == "cookie-auth":
    # Extract token from cookie that was sent during WebSocket upgrade
    # Note: WebSocket upgrade is an HTTP request, so cookies are available
    cookie_token = websocket.cookies.get("access_token")
    if cookie_token:
        token = cookie_token

if not token:
    await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
    return

Option B: Frontend sends actual token in test mode

Modify src/services/api.ts to send the actual token when available:

const tokenMarker = sessionStorage.getItem(TOKEN_STORAGE_KEY);
if (tokenMarker && tokenMarker !== 'cookie-auth') {
  // Test mode: send actual token
  this.ws?.send(JSON.stringify({
    action: 'authenticate',
    token: tokenMarker
  }));
} else {
  // Production: signal cookie auth
  this.ws?.send(JSON.stringify({
    action: 'authenticate',
    session: 'cookie-auth'
  }));
}

Then backend needs to handle both cases.

Testing

  1. Login via OAuth
  2. Navigate to a page that uses WebSocket (dashboard with real-time updates)
  3. Verify WebSocket connection is established and authenticated
  4. Check browser console for WebSocket errors
  5. Verify real-time updates work

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend relatedfrontendFrontend relatedmediumMedium prioritysecuritySecurity vulnerabilities and improvements

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions