Couldn't authenticate WebSocket connection with NullAuthenticator



I’m running into an issue with my Jupyter Notebook when using the NullAuthenticator. I’m leveraging the JupyterHub API to start a server and generate a token for the user. I then navigate to the spawned server’s endpoint using the token http://localhost:8000/user/<username>/?token=<token>. The notebook is visible and seems to work as expected, however attempting to execute a cell generates the error in the console shown below.

WebSocket connection to 'ws://localhost:8000/user/<username>/api/kernels/04218e9c-183c-471a-96ef-02cf3d770e9b/channels?session_id=8eccf31d-173c-496e-ae48-2565bd2a9f49' failed: Error during WebSocket handshake: Unexpected response code: 403

Checking the logs of the notebook instance (running in a container via DockerSpawner) I see that I get the corresponding error.

[W 2023-07-19 16:32:48.175 ServerApp] 403 GET /user/<username>/api/kernels/04218e9c-183c-471a-96ef-02cf3d770e9b/channels?session_id=8eccf31d-173c-496e-ae48-2565bd2a9f49 (@::ffff: 1.41m

If I change the autheticator from null to dummy and don’t provide the token in the URL, then I can run the cells, so I assume it is how I’m generating/configuring the token.


What am I missing that is blocking the websocket from being established?

Supporting Content

JupyterHub Config

c = get_config()

## Network Settings
# we need the hub to listen on all ips when it is in a container
c.JupyterHub.hub_ip = ''
# the hostname/ip that should be used to connect to the hub
# this is usually the hub container's name
c.JupyterHub.hub_connect_ip = 'jupyterhub'

## Authenticator Setting
# Use null authenticator so login is only possible through API
c.JupyterHub.authenticator_class = 'null'

## Spawner Settings
# Use custom spawner
c.JupyterHub.spawner_class = 'filespawner.FileSpawner'

# Network used to communicate with user containers
c.DockerSpawner.network_name = 'jupyterhub'

# Delete containers when they are stopped
c.DockerSpawner.remove = True

## API Settings
# Create the NIST service = [
    { 'name': 'service-custom', 'api_token': '<my token>' }

# Grant NIST service account admin privledges
c.JupyterHub.load_roles = [
        'name': 'service-role',
        'scopes': [
            # Ability to control users
            # Ability to start/stop/delete servers
            # Ability to make tokens
            # See current users
        'services': [
            # assign the service the above permissions

Code Snippet for Token Generation

async function requestToken(user) {
  const response = await fetch(`http://localhost:8000/hub/api/users/${user}/tokens`, {
    method: 'POST',
    headers: AUTH_HEADER

  console.log(await response.text());


Did you find any solution for it?

I am having the same problem

I suspect you are hitting this issue. The workaround is to set:


in the single-user environment to opt-out of the singleuser extension implementation while waiting for a fix.