jupyterhub / jupyterhub

Multi-user server for Jupyter notebooks

Home Page:https://jupyterhub.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Send POST request to `/hub/spawn/` from a service does not work in 4.1.2

trungleduc opened this issue · comments

Bug description

In our tljh-repo2docker service, we have a standalone UI to spawn the server by sending a POST request to the /hub/spawn/ endpoint. It works as expected up to 4.0.x.

From 4.1.2, it does not work anymore with the following error message:

[WebServer]  403 POST /hub/spawn/alice/test-server?_xsrf=MnwxOjB8...TEwODY1MmEwMmMzYQ (127.0.0.1): XSRF cookie does not match POST argument

I think it's related to #4750, since we are sending the _xsrf cookie at path /services/tljh_repo2docker/ but jupyterhub is picking the cookie at /hub/ to do the comparison.

cookies

Is there anything we can do from our side to fix this issue?

Thanks!

How to reproduce

  1. Install a dev version of tljh-repo2docker
  2. Go to http://127.0.0.1:8000/services/tljh_repo2docker/servers then start a new server

Expected behaviour

The POST request finishes without error

Actual behaviour

Your personal set up

  • OS:
  • Version(s):
    jupyterhub 4.1.2
Full environment
# paste output of `pip freeze` or `conda list` here
Configuration
# jupyterhub_config.py
Logs

We inject the xsrf_token to the page via the jinja2 template

class BaseHandler(HubOAuthenticated, web.RequestHandler):
   ...
    async def render_template(self, name: str, **kwargs) -> str:
        template_ns = dict(
            ...
            xsrf_token=self.xsrf_token.decode("ascii"),

        )
        template_ns.update(kwargs)
        template = self.get_template(name)
        return template.render(**template_ns)
   ...

https://github.com/plasmabio/tljh-repo2docker/blob/master/tljh_repo2docker/base.py#L92

Yes, this request shouldn't work, which is fixed in 4.1. Instead, the service should request permission to spawn via OAuth scopes (service.oauth_client_allowed_scopes in config, or be given permission to spawn on behalf of users without any user intervention via role assignment to the service itself) and use the resulting token to make the launch request via the API. Using tokens to authenticate requests eliminates all xsrf issues. The request can be the same originating from the browser, but put the oauth token in the Authorization header instead of the xsrf token in the URL.

c.JupyterHub.services = [
    {
        "name": "myservice",
        "oauth_client_allowed_scopes": ["servers!user"],
        ...
    }
]

Thank @minrk for the pointer! We adopted the new approach and it works nicely.