Using JupyterHub to authenticate to an API-based service

Hi. I have a REST server implemented that offers a generic data service. I would like Hub users to be able to:

  1. Access datasets using the REST API
  2. Publish datasets using the REST API
  3. Query the datasets or obtain lists of what data sets are available
    The server has been implemented standalone, along with JavaScript and Python client libraries. However, I’d like to attach it to JupyterHub as a (preferably managed) service and use JupyterHub to validate the userid’s.
    It’s really clear how to do this if the user is primarily accessing an interactive service. But what I’m struggling with is how to attach authentication to API requests. Ideally, if the user has logged into the hub, the client (which I’ve written and can modify) will pick up the authentication automatically and use it.
    Is there a pointer (ideally, to some sample code) that I can use?
    Oh, and if the request is coming from code running on a Notebook server, e.g. a Notebook kernel, no authentication should be required.
    Many thanks for all of your help.

I think you are after something like this example that runs a simple Flask app as JupyterHub service? There are more examples in JupyterHub repo that should give you some pointers on how to achieve what you are attempting.


Yes, that’s exactly what I had in mind, and many thanks.
I take it that I need to bundle the flask server into the jupyterhub container image?


If you want to run the service as hub managed service, yes, I guess you will need to install the app in JupyterHub image.

But it is possible to run services as external services in which case you can run your flask server in a separate container that is connected to same network as JupyterHub container.

1 Like

The relevant docs are here, but to summarize:

  • you can use JupyterHub as an OAuth provider by registering your service (your service may be “external” - run by you/kubernetes/docker/system/etc. - or “managed” - run by the hub)
  • API requests can be authorized with the header Authorization: Bearer {token}
  • Service can be used to make a request to /hub/api/user with this token to get information about the user and the scopes held by the token. It should have the scope access:services!service=service-name if it has permission to access your service.
  • If you are using Python, the HubOAuth class implements these API requests and loading standard environment variables for URLs, etc.
  • If you are using tornado, the HubOAuthenticated mixin handles the full process of authenticating requests, getting tokens from headers, verifying with JupyterHub

For where to get the token, you have two main choices:

  1. use $JUPYTERHUB_API_TOKEN from the user environment. This is the simplest because it requires no configuration. The scopes for this token are very limited by default, but can be expanded by defining the server role:
# grant $JUPYTERHUB_API_TOKEN API access to this-service
c.JupyterHub.load_roles = [
        "name": "server",
        "scopes": [
            # default scopes
            # additional scopes, access the service

Then, e.g. in a notebook, do:

import os
import requests

s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['JUPYTERHUB_API_TOKEN']}"

r ="https://public-hub-url/services/service-name/api/endpoint")

or 2. issue a dedicated token for this, which will be a bit more fiddly.