Is Jupyterhub RBAC groups the same as OAuth groups?

Hi there,
I’m working with Jupyterhub Collaboration following this doc. In the example of this doc, the members and groups are hard-coded:

projects:
  vox:
    members:
      - vex
      - vax
      - pike
  mighty:
    members:
      - fjord
      - beau
      - jester

In order to not hard-code all the groups and users here, I have a GenericAuthenticator defined, and I expect the Jupyterhub RBAC’s group = Authenticator’s group, but it seems like this is not the case:

GenericOAuthenticator:
      client_id: *
      client_secret: *
      oauth_callback_url: *
      authorize_url: *
      token_url: *
      userdata_url: *
      login_service: Test
      username_claim: preferred_username
      enable_auth_state: true
      scope:  
        - openid 
      claim_groups_key: groups
      admin_groups:
        - admin_group
      allow_all: true
    JupyterHub:
      authenticator_class: generic-oauth
      profile_map = {
          ## I use OAuth group name as profile key
          'jhub_collaboration': make_profile(
            display_name='JupyterHub Collaboration',
            description='JupyterHub Collaboration Environment',
          )
      }
      ## Create collaboration users
      c.JupyterHub.load_roles = []
      c.JupyterHub.load_groups = {
          # collaborative accounts get added to this group
          # so it's easy to see which accounts are collaboration accounts
          "collaborative": [],
      }
      for profile_key in profile_map.keys(): ## I use OAuth group name as profile key
        # define a new user for the collaboration
        collab_user = f"{profile_key}_collab"
        # add the collab user to the 'collaborative' group
        # so we can identify it as a collab account
        c.JupyterHub.load_groups["collaborative"].append(collab_user)

        # finally, grant members of the project collaboration group
        # access to the collab user's server,
        # and the admin UI so they can start/stop the server
        c.JupyterHub.load_roles.append(
            {
                "name": f"collab-access-{profile_key}",
                "scopes": [
                    f"access:servers!user={collab_user}",
                    f"admin:servers!user={collab_user}",
                    "admin-ui",
                    f"list:users!user={collab_user}",
                ],
                "groups": [profile_key], ## I use OAuth group name as profile key
            }
        )

Are Jupyterhub groups and OAuth groups totally different? Is there a way to link/sync them? Can Jupyterhub RBAC to assign role based on OAuth groups?

They’re not the same, but coincidentally someone else has posted the solution :smiley:

1 Like

Oh! Thanks! I’ll try it!

In case it’s useful, here’s a more complete copy of the code I ended up with. I wanted to keep my JH groups in sync with my AWS SSO groups, but you could use this pattern for any OAuth source.

class CustomOAuthenticator(GenericOAuthenticator):
    # Automatically send the user to the OIDC login page instead of prompting them to click a button.
    auto_login = True

    # Allow all AUTHENTICATED users to log in.
    allow_all = True

    # Authenticator class (this) is now responsible for managing groups.
    manage_groups = True

    login_service = ...
    username_claim = ...
    authorize_url = ...
    client_id = ...
    client_secret = ...
    oauth_callback_url ...
    token_url = ...
    userdata_url = ...

    async def update_auth_model(self, auth_model):
        """
        Because manage_groups is set to True, we need to include the user's groups in the returned auth model.
        Ref: https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html#authenticator-managed-group-membership
        """
        auth_model = await super().update_auth_model(auth_model)

        # Build the user's list of groups in the normal fashion. The parent class will do this automatically,
        # but only when checking for admin permissions. We want to do it for all users to return their groups!
        user_info = auth_model["auth_state"][self.user_auth_state_key]
        user_groups = self.get_user_groups(user_info)

        # Include the new "groups" key in the auth_model.
        auth_model["groups"] = user_groups

        return auth_model

    def claim_groups_key(self, user_info):
        """
        Return the JupyterHub groups a user should be associated with:
        - any AWS SSO group membership
        - global collab group
        """
        user_aws_sso_groups = aws_sso.user_groups.get(user_info['email'], [])
        return [shared_group] + list(user_aws_sso_groups)


c.JupyterHub.authenticator_class = CustomOAuthenticator

Once I started down the custom class path, a lot of cool functionality became possible :slight_smile: One problem I did quickly encounter was limitations with the base JupyterHub docker image I was using (via Z2JH), specifically it not having boto3 installed. So, had to customize that as well.

But in the end it works beautifully!

2 Likes