Skip to content

feat(jupyter): forward extra HTTP headers and honor an explicit token - #13

Merged
echarles merged 1 commit into
datalayer:mainfrom
EnyMan:feat/jupyter-sandbox-auth-headers
Jul 29, 2026
Merged

feat(jupyter): forward extra HTTP headers and honor an explicit token#13
echarles merged 1 commit into
datalayer:mainfrom
EnyMan:feat/jupyter-sandbox-auth-headers

Conversation

@EnyMan

@EnyMan EnyMan commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

JupyterSandbox can only authenticate to an external Jupyter Server with a token. Deployments that authenticate with a password have no token — the credentials are a session Cookie plus a matching X-XSRFToken header — and there is currently no way to pass those through the sandbox.

This came up while routing jupyter-mcp-server's password-authentication support (#250) through code-sandboxes after the enh: code sandboxes refactor. It is the same gap that jupyter-nbmodel-client#61 closed for the notebook websocket (additional_headers), now on the kernel side.

The two blockers

  1. Headers could not reach the requests. client_kwargs only reaches the kernel client's websocket. The kernel manager makes its own HTTP requests (kernel GET/POST/DELETE, interrupt) using its headers argument, which nothing populated — and **kwargs were collected into _extra_kwargs and never forwarded to the client.
  2. token=None was overwritten. self._token = token or uuid.uuid4().hex put a credential the server never issued on every request, so cookie-authenticated requests were rejected. A generated token is only meaningful for a server the sandbox starts itself, where it becomes --ServerApp.token.

Changes

  • Add a headers argument to JupyterSandbox, forwarded to:
    • the kernel client (so the manager's HTTP requests carry it),
    • the /api/status readiness poll,
    • the /api/kernels/{id}/interrupt request,
    • the JupyterServerClient used to find a reusable kernel.
  • Only pass headers on when the caller supplied some, so the call is unchanged for the common token/anonymous path.
  • Honor an explicit token as given — including None — when talking to a server the sandbox does not own; still generate one for a server it starts itself.

Tests

Four tests added to tests/test_jupyter.py, in the existing stub style: headers reach the kernel client; no headers kwarg when none supplied (and the token is passed through); an external server keeps token=None; a sandbox-owned server still generates a token.

pytest -q → 108 passed, 2 skipped. ruff check on the changed files is clean (the 8 pre-existing A003 findings elsewhere are untouched).

Compatibility

Backwards compatible: headers defaults to None and is omitted from the client call when unset. The token change only affects the case where a caller explicitly passed no token and supplied a server_url — previously that silently sent a random token, which could not have authenticated anywhere.

🤖 Generated with Claude Code

JupyterSandbox could only authenticate to an external Jupyter Server with a
token. Deployments that authenticate with a password have no token: the
credentials are a session Cookie plus a matching X-XSRFToken header, and there
was no way to pass those through the sandbox.

Two things blocked it:

- Headers could not reach the requests. `client_kwargs` only reaches the kernel
  client's websocket, while the kernel manager sends its own HTTP requests
  (kernel GET/POST/DELETE, interrupt) using its `headers` argument, which
  nothing populated. `**kwargs` were collected into `_extra_kwargs` and never
  forwarded.
- `token=None` was replaced by `uuid.uuid4().hex`, putting a credential the
  server never issued on every request. A generated token is only meaningful
  for a server this sandbox starts itself, where it becomes --ServerApp.token.

So add a `headers` argument, forwarded to the kernel client, to the readiness
and interrupt requests, and to the JupyterServerClient used for kernel reuse.
Only pass it on when the caller supplied headers, so the common
token/anonymous path is unchanged. And keep an explicit token as given —
including None — when talking to a server the sandbox does not own.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@echarles
echarles merged commit 756678f into datalayer:main Jul 29, 2026
8 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