Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User part of multiple groups in keycloak is denied access to Jupyterhub #761

Open
MehdiTantaoui-99 opened this issue Sep 17, 2024 · 2 comments
Labels

Comments

@MehdiTantaoui-99
Copy link

Bug description

I am installing jupyterhub using Helm and Keycloak for authentication. When a user is part of one group (ex: jupyter_users) and I declared in:

allowed_groups:
        - "/jupyter_users"

It works, but if I add that same user to another group (ex: he is part of jupyter_users and foo group in keycloak) then he get denied access to jupyterhub.

How to reproduce

  1. Helm install using the following config:
hub:
  config:
    JupyterHub:
        authenticator_class: generic-oauth
    GenericOAuthenticator:
        login_service: "Keycloak"
        client_id: "$JUPYTERHUB_CLIENT_ID"
        client_secret: "$client_secret"
        oauth_callback_url: "http://$JUPYTERHUB_DOMAIN/hub/oauth_callback"
        authorize_url: "http://$KEYCLOAK_DOMAIN/realms/$KEYCLOAK_REALM/protocol/openid-connect/auth"
        token_url: "http://$KEYCLOAK_DOMAIN/realms/$KEYCLOAK_REALM/protocol/openid-connect/token"
        userdata_url: "http://$KEYCLOAK_DOMAIN/realms/$KEYCLOAK_REALM/protocol/openid-connect/userinfo"
        scope:
        - openid
        - email
        - profile
        - groups
        username_claim: "preferred_username"
        allowed_groups:
        - "/jupyterhub_users"
        admin_groups:
        - "/jupyterhub_admins"
        claim_groups_key: "groups"

singleuser:
    defaultUrl: "/lab"

proxy:
  service:
    type: $SERVICE_TYPE
    nodePorts:
      http: 30696
      https: 30696
  https:
    enabled: true
  1. Create a group in Keycloak called jupyterhub_users
  2. Create a user and add him to group jupyterhub_users
  3. Login to Jupyterhub -> This should work
  4. Create a second group in Keycloak called foo
  5. Add the same user to the group foo
  6. Login to Jupyterhub -> This should throw 500 internal server error
Logs
hub-55f66fc556-g6522 [D 2024-09-17 10:58:43.282 JupyterHub reflector:374] pods watcher timeout
hub-55f66fc556-g6522 [D 2024-09-17 10:58:43.282 JupyterHub reflector:289] Connecting pods watcher
hub-55f66fc556-g6522 [D 2024-09-17 10:58:44.152 JupyterHub log:192] 200 GET /hub/health (@10.244.0.1) 1.29ms
hub-55f66fc556-g6522 [D 2024-09-17 10:58:46.151 JupyterHub log:192] 200 GET /hub/health (@10.244.0.1) 0.83ms
hub-55f66fc556-g6522 [E 2024-09-17 10:58:47.459 JupyterHub oauth2:683] Error Fetching user info... 401 GET http://keycloak.default/realms/omniops/protocol/openid-connect/userinfo: 
hub-55f66fc556-g6522 [E 2024-09-17 10:58:47.460 JupyterHub web:1875] Uncaught exception GET /hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJmZTg1ZWUzODdhZmM0ZDNmOGQxMmVmNWQ4ZDAxMDNmMCJ9&session_state=647de1bc-b9dc-4da9-99a1-ba03190c01ba&iss=http%3A%2F%2Fkeycloak.default%2Frealms%2Fomniops&code=dd3f8514-8d4a-4b76-9b50-065c84135236.647de1bc-b9dc-4da9-99a1-ba03190c01ba.a481311f-ef17-4bb2-8452-8e0566214afb (::ffff:10.244.0.1)
hub-55f66fc556-g6522     HTTPServerRequest(protocol='http', host='jupty', method='GET', uri='/hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJmZTg1ZWUzODdhZmM0ZDNmOGQxMmVmNWQ4ZDAxMDNmMCJ9&session_state=647de1bc-b9dc-4da9-99a1-ba03190c01ba&iss=http%3A%2F%2Fkeycloak.default%2Frealms%2Fomniops&code=dd3f8514-8d4a-4b76-9b50-065c84135236.647de1bc-b9dc-4da9-99a1-ba03190c01ba.a481311f-ef17-4bb2-8452-8e0566214afb', version='HTTP/1.1', remote_ip='::ffff:10.244.0.1')
hub-55f66fc556-g6522     Traceback (most recent call last):
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/tornado/web.py", line 1790, in _execute
hub-55f66fc556-g6522         result = await result
hub-55f66fc556-g6522                  ^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 210, in get
hub-55f66fc556-g6522         user = await self.login_user()
hub-55f66fc556-g6522                ^^^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/jupyterhub/handlers/base.py", line 928, in login_user
hub-55f66fc556-g6522         authenticated = await self.authenticate(data)
hub-55f66fc556-g6522                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/jupyterhub/auth.py", line 493, in get_authenticated_user
hub-55f66fc556-g6522         authenticated = await maybe_future(self.authenticate(handler, data))
hub-55f66fc556-g6522                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 1063, in authenticate
hub-55f66fc556-g6522         user_info = await self.token_to_user(token_info)
hub-55f66fc556-g6522                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 978, in token_to_user
hub-55f66fc556-g6522         return await self.httpfetch(
hub-55f66fc556-g6522                ^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 718, in httpfetch
hub-55f66fc556-g6522         return await self.fetch(
hub-55f66fc556-g6522                ^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 684, in fetch
hub-55f66fc556-g6522         raise e
hub-55f66fc556-g6522       File "/usr/local/lib/python3.11/site-packages/oauthenticator/oauth2.py", line 663, in fetch
hub-55f66fc556-g6522         resp = await self.http_client.fetch(req, **kwargs)
hub-55f66fc556-g6522                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
hub-55f66fc556-g6522     tornado.httpclient.HTTPClientError: HTTP 401: Unauthorized
hub-55f66fc556-g6522     
hub-55f66fc556-g6522 [D 2024-09-17 10:58:47.461 JupyterHub base:1471] No template for 500
hub-55f66fc556-g6522 [E 2024-09-17 10:58:47.475 JupyterHub log:184] {
hub-55f66fc556-g6522       "X-Forwarded-Host": "jupty",
hub-55f66fc556-g6522       "X-Forwarded-Proto": "http",
hub-55f66fc556-g6522       "X-Forwarded-Port": "80",
hub-55f66fc556-g6522       "X-Forwarded-For": "::ffff:10.244.0.1",
hub-55f66fc556-g6522       "Cookie": "_xsrf=[secret]; oauthenticator-state=[secret]",
hub-55f66fc556-g6522       "Accept-Language": "en-US,en;q=0.9",
hub-55f66fc556-g6522       "Accept-Encoding": "gzip, deflate",
hub-55f66fc556-g6522       "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
hub-55f66fc556-g6522       "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
hub-55f66fc556-g6522       "Upgrade-Insecure-Requests": "1",
hub-55f66fc556-g6522       "Cache-Control": "max-age=0",
hub-55f66fc556-g6522       "Connection": "keep-alive",
hub-55f66fc556-g6522       "Host": "jupty"
hub-55f66fc556-g6522     }
hub-55f66fc556-g6522 [E 2024-09-17 10:58:47.475 JupyterHub log:192] 500 GET /hub/oauth_callback?state=[secret]&session_state=[secret]&iss=http%3A%2F%2Fkeycloak.default%2Frealms%2Fomniops&code=[secret] (@::ffff:10.244.0.1) 27.21ms
@consideRatio consideRatio transferred this issue from jupyterhub/helm-chart Sep 17, 2024
@consideRatio
Copy link
Member

This seems like a config issue because of the leading /

        allowed_groups:
        - "/jupyterhub_users"
        admin_groups:
        - "/jupyterhub_admins"

@MehdiTantaoui-99
Copy link
Author

thanks @consideRatio for the reply. No, it works with the / because that's what Keycloak is sending. If the / was the problem it wouldn't work when the user is part of one group only.

We found a workaround which works now, but not sure if this is best practice:

extraConfig: 
    00-custom-authenticator: |
      from oauthenticator.generic import GenericOAuthenticator

      class CustomAuthenticator(GenericOAuthenticator):
          allowed_group = '/jupyter_users'  # Specify your allowed group
          admin_group = '/jupyter_admin'  # Specify your admin group

          async def authenticate(self, handler, data):
              user_info = await super().authenticate(handler, data)
              if user_info:
                  # Get the groups from the token
                  groups = user_info.get('auth_state', {}).get('oauth_user', {}).get('groups', [])
                  print(f"---------{groups}")
                  # Check if the user belongs to the allowed group
                  if self.allowed_group in groups or self.admin_group:
                      return user_info  # Allow login if in allowed group
                  else:
                      return None  # Deny access if not in allowed group

      c.JupyterHub.authenticator_class = CustomAuthenticator
      c.GenericOAuthenticator.scope = ['openid', 'profile', 'email', 'groups']

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants