The JupyterHub single-user server#
When a user logs into JupyterHub, they get a ‘server’, which we usually call the single-user server, because it’s a server that’s meant for a single JupyterHub user. Each JupyterHub user gets a different one (or more than one!).
A single-user server is a process running somewhere that is:
accessible over http[s],
authenticated via JupyterHub using OAuth 2.0,
started by a Spawner, and
‘owned’ by a single JupyterHub user
The single-user server command#
The Spawner’s default single-user server startup command,
jupyter-server, the same program used when you run
jupyter lab on your laptop.
(It can also launch the legacy
That’s why JupyterHub looks familiar to folks who are already using Jupyter at home or elsewhere.
It’s the same!
jupyterhub-singleuser customizes that program to change (approximately) one thing: authenticate requests with JupyterHub.
Single-user server authentication#
Implementation-wise, JupyterHub single-user servers are a special-case of Services
and as such use the same (OAuth) authentication mechanism (more on OAuth in JupyterHub at JupyterHub and OAuth).
This is primarily implemented in the
This code resides in
jupyterhub.singleuser subpackage of JupyterHub.
The main task of this code is to:
resolve a JupyterHub token to a JupyterHub user (authenticate)
check permissions (
access:servers) for the token to make sure the request should be allowed (authorize)
if not authorized, begin the OAuth process with a redirect to the Hub
after login, store OAuth tokens in a cookie only used by this single-user server
implement logout to clear the cookie
Most of this is implemented in the
jupyterhub.singleuser is responsible for adapting the base Jupyter Server to use HubOAuth for these tasks.
JupyterHub authentication extension#
jupyter-server uses its own cookie to authenticate.
If that cookie is not present, the server redirects you a login page and asks you to enter a password or token.
JupyterHub implements these APIs in
The IdentityProvider is responsible for authenticating requests. In JupyterHub, that means extracting OAuth tokens from the request and resolving them to a JupyterHub user.
The Authorizer is a separate API for authorizing actions on particular resources.
Because the JupyterHub IdentityProvider only allows authenticating users who already have the necessary
access:servers permission to access the server, the default Authorizer only contains a redundant check for this same permission, and ignores the resource inputs.
However, specifying a custom Authorizer allows for granular permissions, such as read-only access to subsets of a shared server.
JupyterHub authentication via subclass#
Prior to Jupyter Server 2 (i.e. Jupyter Server 1.x or the legacy
jupyter-notebook server), JupyterHub authentication is applied via subclass.
Originally a subclass of
this approach works with both
Instead of using the extension mechanisms above,
the server application is subclassed. This worked well in the
but doesn’t fit well with Jupyter Server’s extension-based architecture.
Selecting jupyterhub-singleuser implementation#
Using the JupyterHub singleuser-server extension is the default behavior of JupyterHub 4 and Jupyter Server 2, otherwise the subclass approach is taken.
You can opt-out of the extension by setting the environment variable
The subclass approach will also be taken if you’ve opted to use the classic notebook server with:
which was introduced in JupyterHub 2.
jupyterhub-singleuser makes other small customizations to how the single-user server behaves:
logs activity on the single-user server, used in idle-culling.
disables some features that don’t make sense in JupyterHub (trash, retrying ports)
loading options such as URLs and SSL configuration from the environment
customize logging for consistency with JupyterHub logs
Running a single-user server that’s not
jupyterhub-singleuser is the same
jupyter-server used by JupyterLab, Jupyter notebook (>= 7), etc.
But technically, all JupyterHub cares about is that it is:
an http server at the prescribed URL, accessible from the Hub and proxy, and
authenticated via OAuth with the Hub (it doesn’t even have to do this, if you want to do your own authentication, as is done in BinderHub)
which means that you can customize JupyterHub to launch any web application that meets these criteria, by following the specifications in Services.
Most of the time, though, it’s easier to use jupyter-server-proxy if you want to launch additional web applications in JupyterHub.