Skip to content

BUG: do not move api_key to a cookie on WebSocket connections - #99

Merged
dmgav merged 1 commit into
bluesky:mainfrom
SeijDeLeon:fix-websocket-api-key-cookie
Oct 18, 2026
Merged

dmgav merged 1 commit into
bluesky:mainfrom
SeijDeLeon:fix-websocket-api-key-cookie

Conversation

@SeijDeLeon

Copy link
Copy Markdown
Contributor

The set_cookies HTTP middleware initializes request.state.cookies_to_set, but that middleware does not run for WebSocket connections. move_api_key() assumed the attribute was always present, so authenticating a WebSocket with ?api_key=... raised an AttributeError. Skip setting the cookie when there is no place to store it, and add regression tests covering both paths.

Description

move_api_key() unconditionally appended to request.state.cookies_to_set.
That attribute is initialized by the set_cookies HTTP middleware in
app.py, which is registered with @app.middleware("http") and therefore
never runs for WebSocket connections. On the WebSocket path the attribute
does not exist, so the append raised AttributeError.

move_api_key() now checks for the attribute and skips setting the cookie
when it is absent, logging at debug level. The HTTP path is unchanged.

Motivation and Context

Opening a WebSocket with the API key in the query string — e.g.
/console_output/ws?api_key=... — fails with an AttributeError.

The path is: get_current_principal_websocket() reads api_key from
websocket.query_params and calls get_current_principal(), which calls
move_api_key() after resolving the principal. Because move_api_key() only
acts when "api_key" in request.query_params, the header form
(Authorization: ApiKey ...) and the first-message {"type": "auth"}
handshake are unaffected — only the query-parameter form reaches the bug.

The AttributeError is also not contained: get_current_principal_websocket()
catches HTTPException only, so it propagates out of the socket route rather
than surfacing as a clean auth failure.

Setting a cookie is meaningless on a WebSocket handshake in any case — there is
no response object to attach it to. Browsers that open a socket with
?api_key=... should establish the cookie via an ordinary HTTP request.

Addresses #94

Summary of Changes for Release Notes

Fixed

  • Fixed AttributeError when authenticating a WebSocket connection with an
    API key passed as a query parameter (e.g. /console_output/ws?api_key=...).

How Has This Been Tested?

Added two regression tests to
src/bluesky_httpserver/tests/test_auth_for_websockets.py, driving
move_api_key() with a real starlette.datastructures.State:

  • test_move_api_key_sets_cookie_on_http_request — with cookies_to_set
    initialized, the cookie is still queued (guards against regressing the HTTP
    path).
  • test_move_api_key_skips_cookie_on_websocket_request — with the attribute
    absent, the call returns without raising and sets nothing.

Also tested this by hand in a web browser with Finch and using Postman.

The `set_cookies` HTTP middleware initializes `request.state.cookies_to_set`,
but that middleware does not run for WebSocket connections. `move_api_key()`
assumed the attribute was always present, so authenticating a WebSocket with
`?api_key=...` raised an AttributeError. Skip setting the cookie when there is
no place to store it, and add regression tests covering both paths.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@SeijDeLeon
SeijDeLeon requested a review from dmgav October 16, 2026 23:02
@dmgav
dmgav merged commit 401aece into bluesky:main Oct 18, 2026
6 checks passed
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