Make JupyterHub authentication pluggable

As discussed in oauthenticator, I believe a MultiAuthenticator can work today with the JupyterHub APIs, following the example of wrapspawner and satisfy all of these requirements. It would be simplest if they were all oauth providers, and I think that’s a sensible requirement, but not strictly necessary.

Essentially, a MultiAuthenticator would be a proxy implementation for a collection of any number of Authenticator classes. There is already a proof of concept that works today. The main things it would need to do:

  1. custom login html to offer multiple links instead of just one
  2. record which sub-authenticator class is chosen in auth state
  3. wrap returned usernames to avoid collisions (could be fancy, and allow users to pick and map on successful login, or could be simple and apply a provider prefix/suffix.)

However, for the most part, oauth providers are consistent with a collection of URLs for configuration, and can be relatively brief configurations of GenericOAuthenticator (a few urls and a scope list).

It may well make sense to promote the ‘collection of authenticators’ concept to a core JupyterHub feature (i.e. multiple Authenticators instead of one), but I think it’s best to implement it as MultiAuthenticator first. I don’t believe there are any current impediments to this, but the way to start is to try building it and see if/where that’s true. I think it’s appropriate to do this in the OAuthenticator package.

Looking at python-social-auth and django-allauth, they aren’t substantially different from OAuthenticator, but some folks have gone through all the tedious work to define lots of providers, which is great. I think it would be super useful to investigate consuming the python-social-auth API to generate authenticators so we don’t need to duplicate everything in OAuthenticator. If it goes well, maybe it should even be a dependency and we can reduce a lot of the redundant definitions we have.

So I see these main tasks to explore:

  1. implement MultiAuthenticator prototype in OAuthenticator, and test it out
  2. implement SocialAuthenticator that takes a name from python-social-auth and adapts it to the Authenticator API. The biggest impediment seems to be that social-auth is all sync APIs, so we’d need to wrap it in @run_in_executor to put blocking requests in a background thread.
  3. investigate python-social-auth to see if there are any other common APIs we should be making more consistent across our OAuthenticators.

If intrepid folks were really ambitious, this could start as a new package meant to be a successor to OAuthenticator, but not developing the original package. I think either approach is fine and has trade-offs - a new package means some repeated work, but minimal backward-compatibility concerns.

4 Likes