Adding 'SameSite' to 'tornado_settings.cookie_options' causes an error

When adding the SameSite option to c.JupyterHub.tornado_settings, an error occurs while spawning a singleuser notebook.

I am using the helm chart release version 3.0.3.

The contents of the config.yaml file are as follows.

hub:
  baseUrl: /jupyter
  config:
    Authenticator:
      admin_users:
        - root
        - "542380"
    JupyterHub:
      authenticator_class: "dummy"
  extraConfig:
    hub: |
      c.JupyterHub.tornado_settings = { "headers": { "Content-Security-Policy": "frame-ancestors * 'self' https://mysite.com", "Access-Control-Allow-Origin": "https://mysite.com"}, "cookie_options": {"SameSite": "None", "Secure": True}}
    spawner: >-
      c.Spawner.args = ["--ServerApp.tornado_settings={\"headers\":{\"Content-Security-Policy\": \"frame-ancestors * 'self' https://mysite.com\"}}"]
  services:
singleuser:
  storage:
    capacity: 2Gi

The error output by the hub pod is as follows.

[I 2023-09-12 06:15:59.355 JupyterHub app:3247] JupyterHub is now running, internal Hub API at http://hub:8081/jupyter/hub/
[I 2023-09-12 06:16:00.163 JupyterHub log:191] 200 GET /jupyter/hub/api/ (jupyterhub-idle-culler@::1) 10.80ms
[I 2023-09-12 06:16:00.172 JupyterHub log:191] 200 GET /jupyter/hub/api/users?state=[secret] (jupyterhub-idle-culler@::1) 7.44ms
[I 2023-09-12 06:16:05.127 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.7.138) 0.71ms
[I 2023-09-12 06:16:05.130 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.26.118) 2.16ms
[I 2023-09-12 06:16:20.135 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.7.138) 0.61ms
[I 2023-09-12 06:16:20.137 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.26.118) 0.39ms
[I 2023-09-12 06:16:35.138 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.7.138) 0.48ms
[I 2023-09-12 06:16:35.139 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.26.118) 0.36ms
[I 2023-09-12 06:16:50.142 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.7.138) 0.54ms
[I 2023-09-12 06:16:50.144 JupyterHub log:191] 301 GET /jupyter/hub -> /jupyter/hub/ (@::ffff:10.0.26.118) 0.86ms
[I 2023-09-12 06:16:52.197 JupyterHub log:191] 302 GET /jupyter -> /jupyter/hub/ (@::ffff:10.0.26.118) 0.64ms
[I 2023-09-12 06:16:52.215 JupyterHub log:191] 302 GET /jupyter/hub/ -> /jupyter/hub/login?next=%2Fjupyter%2Fhub%2F (@::ffff:10.0.26.118) 0.75ms
[I 2023-09-12 06:16:52.262 JupyterHub log:191] 200 GET /jupyter/hub/login?next=%2Fjupyter%2Fhub%2F (@::ffff:10.0.26.118) 27.39ms
[I 2023-09-12 06:16:54.728 JupyterHub base:837] User logged in: root
[I 2023-09-12 06:16:54.729 JupyterHub log:191] 302 POST /jupyter/hub/login?next=%2Fjupyter%2Fhub%2F -> /jupyter/hub/ (root@::ffff:10.0.26.118) 24.21ms
[I 2023-09-12 06:16:54.776 JupyterHub log:191] 302 GET /jupyter/hub/ -> /jupyter/hub/spawn (root@::ffff:10.0.26.118) 18.51ms
[I 2023-09-12 06:16:54.825 JupyterHub provider:659] Creating oauth client jupyterhub-user-root
[I 2023-09-12 06:16:54.868 JupyterHub reflector:274] watching for pods with label selector='component=singleuser-server' in namespace jh
[I 2023-09-12 06:16:54.891 JupyterHub reflector:274] watching for events with field selector='involvedObject.kind=Pod' in namespace jh
[I 2023-09-12 06:16:54.894 JupyterHub spawner:2519] Attempting to create pvc claim-root, with timeout 3
[I 2023-09-12 06:16:54.932 JupyterHub spawner:2535] PVC claim-root already exists, so did not create new pvc.
[E 2023-09-12 06:16:54.933 JupyterHub user:884] Unhandled error starting root's server: '"SameSite"'
    Traceback (most recent call last):
      File "/usr/local/lib/python3.11/site-packages/jupyterhub/user.py", line 798, in spawn
        url = await gen.with_timeout(timedelta(seconds=spawner.start_timeout), f)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 2679, in _start
        pod = await self.get_pod_manifest()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 1996, in get_pod_manifest
        env=self._expand_all(self.get_env()),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 1829, in _expand_all
        return {k: self._expand_all(v) for k, v in src.items()}
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 1829, in <dictcomp>
        return {k: self._expand_all(v) for k, v in src.items()}
                   ^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 1831, in _expand_all
        return self._expand_user_properties(src)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.11/site-packages/kubespawner/spawner.py", line 1812, in _expand_user_properties
        rendered = template.format(
                   ^^^^^^^^^^^^^^^^
    KeyError: '"SameSite"'

I’m not sure what part is wrong. The same issue occurs with chart version 3.0.2.

I have similar settings for cookie_options

Also getting the same KeyError: ‘“SameSite”’; if removing SameSite, will get key error on “Secure”

Tested with helm chart release version 2.0.0, 3.0.0 and 3.0.3, all have the same error.

Help needed please. Thanks so much!

I believe you are hitting this bug, which I think will be fixed by Support lists and dicts as values in `kubespawner_override` by yuvipanda · Pull Request #785 · jupyterhub/kubespawner · GitHub. I’m not sure if there’s a workaround.

You might be able to set:

c.Spawner.environment.update(
{"JUPYTERHUB_COOKIE_OPTIONS": "{%s}" % json.dumps(cookie_options)}
)

to properly escape the cookie options environment.

You may want to check the cookie_options values, though. These are passed to tornado’s set_cookie, and the keys should be lowercase.