Jupyterhub Custom API Token Auth


By default Jupyterhub manages it’s own API tokens (creates/stores/authentication procedure). I have a collection of tokens for users I’d like to re use, but not 100% sure how I’d go about it.
Already using a custom Authenticator with it’s own handler of /login but it doesn’t handle the API token auth/login.
So just wanted to know if there are any examples or recommended paths I should take?


Hi! It’s not clear to me what you’re trying to do. Could you explain a bit more, e.g. describe the full workflow from the point of view of a user, and what you’re doing with the pre-defined tokens?

Hey! thanks for the quick reply,

workflow from the point of view of a user, and what you’re doing with the pre-defined tokens?

Use case:

  • User A want’s to use the hub API

Default behavior:

  • Create a token for user A in the hub → jupyterhub token < username >
  • Use this token for users A api interactions

Objective is:

  • Use external service token/or custom auth for users A api interations. Want to skip having to create a hub token for user A, if this user can use a token of an external service to validate him/authenticate. Makes better sense?


Could you expand on what API calls you’re talking about? Are these calls being made by e.g. a JupyterLab extension, or are you calling the JupyterHub API from a notebook, or from a terminal?

JupyterHub cannot in general allow access to its API via tokens it does not control (relations in the SQL database are necessary to resolve permissions, etc.). What you can do is authenticate with your tokens using a custom Authenticator, and then issue a JupyterHub token for subsequent requests as part of that process so that the process is transparent.

You can’t skip issuing the token, but you can skip the user having to visit the page and click the button by registering a Handler, which is perhaps the most directly relevant source of friction.

One thing that is perhaps relevant is that the POST /api/tokens/:user API request does accept an ‘auth’ field, which is passed along with the RequestHandler itself to Authenticator.authenticate(handler, req.body.auth):

from jupyterhub.auth import Authenticator

class TokenAuthenticator(Authenticator):
    def authenticate(self, handler, data):
        # could also get token from handler.headers, etc.
        token = data.get("token")
        if token == "super-secret":
            return data["username"]

c.JupyterHub.authenticator_class = TokenAuthenticator

c.Authenticator.allowed_users = {'testuser'}

which can be used to exchange the “external” token super-secret for a JupyterHub token with:

 curl -X POST -d '{"auth": {"username": "testuser", "token": "super-secret"}}' | jq -r '.token'

For a pattern like this, it may make sense to issue tokens with a short expiration, or even operate your own token-exchange endpoint to make it more convenient.

Thanks for your help!