Skip to content

[Bug]: WebSocket Realtime API authentication failure - Headers not read during handshake 🎄🎅 #18456

Description

@RedaBenh

What happened?

LiteLLM proxy does not read authentication headers during WebSocket handshake for the /v1/realtime endpoint.

Actual Behavior

  • WebSocket connection to /v1/realtime is accepted
  • Connection immediately closes with error 403: "No API key provided"
  • Server logs show "Exception: No api key passed in" (3 times)
  • Authentication headers (Authorization, api-key) are completely ignored during WebSocket handshake

Expected Behavior

LiteLLM should read the authentication headers during WebSocket handshake and:

  1. Accept the client WebSocket connection ✅ (works)
  2. Validate the API key from headers ❌ (fails - headers not read)
  3. Forward the connection to OpenAI with proper authentication
  4. Maintain bidirectional WebSocket stream for real-time audio
  5. Track usage and apply quota management

Critical Finding

The same API key works perfectly for HTTP endpoints (/key/info, /v1/chat/completions) but fails for WebSocket connections. This proves:

  • The API key is valid
  • The issue is specific to WebSocket header handling
  • Only the /v1/realtime endpoint is affected (all HTTP endpoints work fine)

Reproduction Steps

1. LiteLLM Configuration (litellm_config.yaml):

model_list:
  - model_name: gpt-realtime-mini
    litellm_params:
      model: openai/gpt-4o-realtime-preview
      api_key: os.environ/OPENAI_API_KEY

files_settings:
  - custom_llm_provider: openai
    api_key: os.environ/OPENAI_API_KEY

2. Docker Compose setup:

services:
  litellm:
    image: ghcr.io/berriai/litellm:main-v1.80.11
    environment:
      - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    ports:
      - "4000:4000"
    volumes:
      - ./litellm_config.yaml:/app/config.yaml

3. Create a virtual API key via LiteLLM UI

4. Test HTTP authentication (to verify key is valid):

curl -X GET "https://your-server.com/key/info" \
  -H "Authorization: Bearer sk-your-key"

Result: ✅ 200 OK - Returns key info (API key is valid)

5. Test WebSocket with same API key:

import asyncio
import websockets

async def test_realtime():
    api_key = "sk-your-api-key-here"  # Same key that works for HTTP
    url = "wss://your-litellm-server.com/v1/realtime?model=gpt-realtime-mini"

    # Try all authentication methods
    headers = {
        "Authorization": f"Bearer {api_key}",
        "api-key": api_key,
        "OpenAI-Beta": "realtime=v1"
    }

    async with websockets.connect(url, extra_headers=headers) as ws:
        response = await ws.recv()
        print(f"Received: {response}")

asyncio.run(test_realtime())

Result: ❌ WebSocket closes with 403 "No API key provided"

Root Cause Analysis

LiteLLM's WebSocket endpoint (/v1/realtime) cannot read custom HTTP headers during the WebSocket handshake.

Technical details:

  • FastAPI/Starlette limitation: Custom headers (Authorization, api-key) sent during WebSocket upgrade request are not accessible via the standard WebSocket API
  • Authentication check location: user_api_key_auth.py:632 in _user_api_key_auth_builder()
  • LiteLLM tries to validate the key 3 times via GET /key/info?key=sk-xxx but the key parameter is never extracted from headers

What works:

  • ✅ HTTP authentication with Authorization header (/key/info, /v1/chat/completions)
  • ✅ Standard chat completions with streaming (SSE)
  • ✅ Embeddings API
  • ✅ All REST HTTP endpoints

What doesn't work:

  • ❌ WebSocket /v1/realtime with Authorization header
  • ❌ WebSocket /v1/realtime with api-key header
  • ❌ Any combination of authentication headers in WebSocket handshake

Attempted workarounds (all failed):

  1. Multiple header formats: Authorization, api-key, x-litellm-api-key - none are read
  2. WebSocket subprotocols: OpenAI-style openai-insecure-api-key.{API_KEY} - not supported
  3. Different WebSocket client libraries - headers never reach LiteLLM auth layer

Related Issues

  • #6825 - WebSocket issues with OpenAI Realtime API in browser (marked "not planned")
  • #6926 - /v1/realtime endpoint not checking API key (vulnerability fixed in v1.80.5, but headers still not working)

Environment

  • OS: Linux (Docker container)
  • Deployment: Docker Compose
  • LiteLLM version: v1.80.11
  • Docker image: ghcr.io/berriai/litellm:main-v1.80.11
  • Python: 3.13 (in container)
  • websockets library: 13.1
  • OpenAI API: Works correctly when accessed directly (bypassing LiteLLM confirms the issue is in LiteLLM, not upstream)

Relevant log output

INFO:     10.x.x.x:xxxxx - "GET /key/info?key=sk-xxxx HTTP/1.1" 401 Unauthorized
12:59:18 - LiteLLM Proxy:ERROR: auth_exception_handler.py:79 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured - No api key passed in.
Requester IP Address:10.x.x.x
Traceback (most recent call last):
  File "/usr/lib/python3.13/site-packages/litellm/proxy/auth/user_api_key_auth.py", line 632, in _user_api_key_auth_builder
    raise Exception("No api key passed in.")
Exception: No api key passed in.

INFO:     10.x.x.x:xxxxx - "GET /key/info?key=sk-xxxx HTTP/1.1" 401 Unauthorized
12:59:18 - LiteLLM Proxy:ERROR: auth_exception_handler.py:79 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured - No api key passed in.
Requester IP Address:10.x.x.x
Traceback (most recent call last):
  File "/usr/lib/python3.13/site-packages/litellm/proxy/auth/user_api_key_auth.py", line 632, in _user_api_key_auth_builder
    raise Exception("No api key passed in.")
Exception: No api key passed in.

INFO:     10.x.x.x:xxxxx - "GET /key/info?key=sk-xxxx HTTP/1.1" 401 Unauthorized
12:59:18 - LiteLLM Proxy:ERROR: auth_exception_handler.py:79 - litellm.proxy.proxy_server.user_api_key_auth(): Exception occured - No api key passed in.
Requester IP Address:10.x.x.x
Traceback (most recent call last):
  File "/usr/lib/python3.13/site-packages/litellm/proxy/auth/user_api_key_auth.py", line 632, in _user_api_key_auth_builder
    raise Exception("No api key passed in.")
Exception: No api key passed in.

INFO:     ('10.x.x.x', xxxxx) - "WebSocket /v1/realtime?model=gpt-realtime-mini" [accepted]
INFO:     connection open
INFO:     connection closed

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.80.11

Twitter / LinkedIn details

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions