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:
- custom login html to offer multiple links instead of just one
- record which sub-authenticator class is chosen in auth state
- 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:
- implement MultiAuthenticator prototype in OAuthenticator, and test it out
- 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. - 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.