User are mapping to the same folder

Hi All,

When using Globus authentication, I am having an issue with a user logging into a different user in JupyterHub. For example, a user with james@gmail.com and a user with james@globus.org login, both users are mapped to the same user (James) in JupyterHub. I have also found a similar post on Github “[Globus] Users with the same userid in different identity domains map to the same user · Issue #527 · jupyterhub/oauthenticator · GitHub”. However, there’s no solution yet to it. Some suggestions or recommendations are greatly appreciated.

Best,
James

You will have to override user_info_to_username method. Based on the domain the user is coming from, you will need to ensure to create a unique username.

Thanks @mahendrapaipuri, it was pretty helpful. I was able to set the username as the email. However, when I logged in the first time, JupyterHub created an account for me and logged in to it. After a while, when I tried with a totally new account, JupyterHub created a new user locally but was not able to redirect to the notebook. I am getting 404 pages not found, but I was logged in. The below image is the error I get.

404 error

I have set the below configuration in my jupyterhub_config.py for creating the user account locally based on the email. I am not entirely sure if those are the only configurations I have to make in order to allow users from different domains to access different accounts. Thanks for any help or suggestions given.

c.Authenticator.post_auth_hook = my_hook
def my_hook(authenticator, handler, authentication):
   try:
       username = authentication['auth_state']['globus_user']['preferred_username']
       delimiters = ["@", "."]
       username = ".".join(username.split("@")[:2])
       for delimiter in delimiters:
           username = "_".join(username.split(delimiter))
       check_call(['useradd', '-ms', '/bin/bash', username])
   except Exception as err:
       send_info(f"user exist!")
   spawn_data = {
       'pw_data': user_data,
       'gid_list': os.getgrouplist(username, user_data.pw_gid)
   }
   if authentication['auth_state'] is None:
       authentication['auth_state'] = {}

   auth_state_info = authentication['auth_state']
   authentication['auth_state']['spawn_data'] = spawn_data
   return authentication

c.OAuthenticator.enable_auth_state = True

Which spawner are you using? Could you share the logs of JupyterHub?

I think it would be easier to maintain if you create JupyterHub users with the same username that you are using to create Linux users. If I understood you well, you are using email as username in JupyterHub but a “normalized” username for Linux system users. Not sure how you are mapping your JupyterHub user to the Linux system user though.

So you can put the logic that you are using in hook to create unique username in userinfo_to_username method directly and then use LocalGlobusOAuthenticator which will create system user with same name as JupyterHub’s username.

1 Like

Hi @mahendrapaipuri
Thanks for your reply, I believe I am using the default spawner which is LocalProcessSpawner. After modifying the function user_info_to_username directly, it is still somehow giving me the same error. I have checked that I am creating linux system user with the email.

Below is the full configuration to my jupyterhub_config.py

import os, pwd
import inspect, concurrent,asyncio
from subprocess import check_call

c.OAuthenticator.authorize_url = "https://auth.globus.org/v2/oauth2/authorize"
c.OAuthenticator.token_url = "https://auth.globus.org/v2/oauth2/token"
c.OAuthenticator.userdata_url = "https://auth.globus.org/v2/oauth2/userinfo"
c.OAuthenticator.allow_all = True
c.JupyterHub.authenticator_class = "globus"
c.OAuthenticator.oauth_callback_url = "http://127.0.0.1:8888/hub/oauth_callback"
c.OAuthenticator.client_id = ""
c.OAuthenticator.client_secret = ""
c.OAuthenticator.enable_auth_state = True

if 'JUPYTERHUB_CRYPT_KEY' not in os.environ:
    c.CryptKeeper.keys = [ os.urandom(32) ]

c.LocalGlobusOAuthenticator.add_user_cmd = ['adduser', '-q', '–gecos', '""', '–disabled-password']
c.LocalAuthenticator.create_system_users = True
c.OAuthenticator.scope = ['openid', 'profile', 'email']
c.OAuthenticator.exclude_tokens = ['auth.globus.org']
c.OAuthenticator.logout_redirect_url = 'https://globus.org/logout'
c.OAuthenticator.revoke_tokens_on_logout = False
c = get_config()
c.JupyterHub.admin_access = True
c.JupyterHub.base_url = '/'
c.JupyterHub.bind_url = 'http://127.0.0.1:8888'
c.JupyterHub.data_files_path = '/home/controller/jupyterhub/share/jupyterhub'
c.JupyterHub.extra_log_file = '/var/log/jupyterhub.log'
c.JupyterHub.hub_ip = '127.0.0.1'
c.JupyterHub.hub_port = 8087
c.ConfigurableHTTPProxy.api_url = 'http://127.0.0.1:5432'
c.Spawner.debug = True
c.Spawner.ip = '127.0.0.1'
c.Spawner.port = 8886
c.Authenticator.auto_login = True

Below is the log I get from jupyterhub.log

[D 2024-03-12 11:31:31.610 JupyterHub proxy:880] Proxy: Fetching GET http://127.0.0.1:5432/api/routes
[D 2024-03-12 11:31:31.614 JupyterHub proxy:392] Checking routes
[D 2024-03-12 11:32:42.049 JupyterHub base:344] Refreshing auth for james_google_com
[D 2024-03-12 11:32:42.049 JupyterHub scopes:877] Checking access to /hub/api/users/james_google_com/activity via scope users:activity
[D 2024-03-12 11:32:42.049 JupyterHub scopes:690] Argument-based access to /hub/api/users/james_google_com/activity via users:activity
[D 2024-03-12 11:32:42.050 JupyterHub users:879] Not updating activity for <User(james_google_com 1/1 running)>: 2024-03-12T16:22:39.858483Z < 2024-03-12T16:23:14.811000Z
[D 2024-03-12 11:32:42.050 JupyterHub users:900] Not updating server activity on james_google_com/: 2024-03-12T16:22:39.858483Z < 2024-03-12T16:23:14.811000Z
[I 2024-03-12 11:32:42.051 JupyterHub log:191] 200 POST /hub/api/users/james_google_com/activity (james_google_com@127.0.0.1) 23.81ms
[I 2024-03-12 11:32:59.502 JupyterHub log:191] 302 GET / -> /hub/ (@127.0.0.1) 1.61ms
[I 2024-03-12 11:32:59.513 JupyterHub log:191] 302 GET /hub/ -> /hub/login?next=%2Fhub%2F (@127.0.0.1) 2.16ms
[I 2024-03-12 11:32:59.529 JupyterHub log:191] 302 GET /hub/login?next=%2Fhub%2F -> /hub/oauth_login?next=%2Fhub%2F (@127.0.0.1) 1.33ms
[I 2024-03-12 11:32:59.537 JupyterHub oauth2:97] OAuth redirect: http://127.0.0.1:8888/hub/oauth_callback
[D 2024-03-12 11:32:59.537 JupyterHub base:587] Setting cookie oauthenticator-state: {'httponly': True, 'expires_days': 1}
[I 2024-03-12 11:32:59.538 JupyterHub log:191] 302 GET /hub/oauth_login?next=%2Fhub%2F -> https://auth.globus.org/v2/oauth2/authorize?response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A8888%2Fhub%2Foauth_callback&client_id=<some client id>&state=[secret]&scope=openid+profile+email (@127.0.0.1) 1.22ms
[I 2024-03-12 11:33:08.579 JupyterHub log:191] 302 GET / -> /hub/ (@127.0.0.1) 1.40ms
[I 2024-03-12 11:33:08.595 JupyterHub log:191] 302 GET /hub/ -> /hub/login?next=%2Fhub%2F (@127.0.0.1) 0.71ms
[I 2024-03-12 11:33:08.601 JupyterHub log:191] 302 GET /hub/login?next=%2Fhub%2F -> /hub/oauth_login?next=%2Fhub%2F (@127.0.0.1) 0.66ms
[I 2024-03-12 11:33:08.606 JupyterHub oauth2:97] OAuth redirect: http://127.0.0.1:8888/hub/oauth_callback
[D 2024-03-12 11:33:08.607 JupyterHub base:587] Setting cookie oauthenticator-state: {'httponly': True, 'expires_days': 1}
[I 2024-03-12 11:33:08.607 JupyterHub log:191] 302 GET /hub/oauth_login?next=%2Fhub%2F -> https://auth.globus.org/v2/oauth2/authorize?response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A8888%2Fhub%2Foauth_callback&client_id=<some client id>&state=[secret]&scope=openid+profile+email (@127.0.0.1) 0.98ms
[D 2024-03-12 11:33:25.593 JupyterHub oauth2:979] Refresh token was empty, will try to pull refresh_token from previous auth_state
[D 2024-03-12 11:33:25.595 JupyterHub oauth2:789] Encrypted_auth_state was found, will try to decrypt and pull refresh_token from it...
[D 2024-03-12 11:33:25.598 JupyterHub roles:281] Assigning default role to User james_globusid_org
[D 2024-03-12 11:33:25.611 JupyterHub base:587] Setting cookie jupyterhub-session-id: {'httponly': True, 'path': '/'}
[D 2024-03-12 11:33:25.611 JupyterHub base:591] Setting cookie for james_globusid_org: jupyterhub-hub-login
[D 2024-03-12 11:33:25.611 JupyterHub base:587] Setting cookie jupyterhub-hub-login: {'httponly': True, 'path': '/hub/'}
[I 2024-03-12 11:33:25.612 JupyterHub base:837] User logged in: james_globusid_org
[I 2024-03-12 11:33:25.612 JupyterHub log:191] 302 GET /hub/oauth_callback?code=[secret]&state=[secret] -> /hub/ (@127.0.0.1) 455.88ms
[D 2024-03-12 11:33:25.634 JupyterHub user:431] Creating <class 'jupyterhub.spawner.LocalProcessSpawner'> for james_globusid_org:
[I 2024-03-12 11:33:25.635 JupyterHub log:191] 302 GET /hub/ -> /hub/spawn (james_globusid_org@127.0.0.1) 13.55ms
[D 2024-03-12 11:33:25.640 JupyterHub scopes:877] Checking access to /hub/spawn via scope servers
[D 2024-03-12 11:33:25.640 JupyterHub scopes:690] Argument-based access to /hub/spawn via servers
[D 2024-03-12 11:33:25.641 JupyterHub pages:217] Triggering spawn with default options for james_globusid_org
[D 2024-03-12 11:33:25.641 JupyterHub base:344] Refreshing auth for james_globusid_org
[D 2024-03-12 11:33:25.641 JupyterHub base:961] Initiating spawn for james_globusid_org
[D 2024-03-12 11:33:25.641 JupyterHub base:965] 0/100 concurrent spawns
[D 2024-03-12 11:33:25.641 JupyterHub base:970] 1 active servers
[I 2024-03-12 11:33:25.673 JupyterHub provider:659] Creating oauth client jupyterhub-user-james_globusid_org
[D 2024-03-12 11:33:25.706 JupyterHub user:794] Calling Spawner.start for james_globusid_org
[I 2024-03-12 11:33:25.707 JupyterHub spawner:1689] Spawning jupyterhub-singleuser
[D 2024-03-12 11:33:25.727 JupyterHub spawner:1384] Polling subprocess every 30s
[D 2024-03-12 11:33:25.740 JupyterHub utils:278] Server at http://127.0.0.1:8886/user/james_globusid_org/ responded with 404
[D 2024-03-12 11:33:25.741 JupyterHub _version:74] jupyterhub and jupyterhub-singleuser both on version 4.0.2
[I 2024-03-12 11:33:25.741 JupyterHub base:990] User james_globusid_org took 0.100 seconds to start
[I 2024-03-12 11:33:25.741 JupyterHub proxy:330] Adding user james_globusid_org to proxy /user/james_globusid_org/ => http://127.0.0.1:8886
[D 2024-03-12 11:33:25.741 JupyterHub proxy:880] Proxy: Fetching POST http://127.0.0.1:5432/api/routes/user/james_globusid_org
[I 2024-03-12 11:33:25.744 JupyterHub log:191] 302 GET /hub/spawn -> /hub/spawn-pending/james_globusid_org (james_globusid_org@127.0.0.1) 105.57ms
[D 2024-03-12 11:33:25.749 JupyterHub scopes:877] Checking access to /hub/spawn-pending/james_globusid_org via scope servers
[D 2024-03-12 11:33:25.750 JupyterHub scopes:690] Argument-based access to /hub/spawn-pending/james_globusid_org via servers
[I 2024-03-12 11:33:25.750 JupyterHub log:191] 302 GET /hub/spawn-pending/james_globusid_org -> /user/james_globusid_org/ (james_globusid_org@127.0.0.1) 3.14ms
[I 2024-03-12 11:33:27.438 JupyterHub log:191] 200 GET /hub/api (@127.0.0.1) 0.54ms

I am not sure if my setup is only allowing a single user login, which blocks out other user from logging in. After carefully looking into the log, I noticed even after logging out from the previous account, it is still checking for an update. Is this running in the right behavior?

From your logs you have a user james_gmail_com already running and server and I understand you are logging in as another user james_globusid_org. And from this line

JupyterHub is redirecting you to correct resource of the user james_globusid_org which is expected. Aren’t you not being redirected to correct server in the browser?

Just ensure you clear all cookies in the browser and do these sort of tests in incognito sessions just not to mess up the cookies.

Hi @mahendrapaipuri,

Thank for the quick response. I am testing all the login in incognito sessions.

Scenario:
When I restarted the jupyterhub, and login to an account (james@gmail.com) in incognito session, it is allowing me to login to the correct user. Once I am done logging in, I closed the browser and opened a new incognito session. This time, I login to another account (james@globus.org), and I am getting the below error.

Cheers for the info! Well, from the URL in screenshot, it is correct user’s resource. So, there is no issue on the authenticator side I guess.

How are you managing your Python environments for JupyterHub and single user server (JupyterLab and/or notebook)? Does this newly created user have permissions to access the single user server Python environment? Can you share the logs of the single user server?

Hey @mahendrapaipuri,

The above configurations are what I have so far, I didn’t really configure much for my spawner other than the ip and port. I have setup the singleuser log, and I am getting the below error.

[I 2024-03-12 13:39:49.822 ServerApp] jupyter_lsp | extension was successfully linked.
[I 2024-03-12 13:39:49.827 ServerApp] jupyter_server_terminals | extension was successfully linked.
[I 2024-03-12 13:39:49.827 JupyterHubSingleUser] Starting jupyterhub single-user server extension version 4.0.2
[E 2024-03-12 13:39:49.827 JupyterHubSingleUser] Failed to load JupyterHubSingleUser server extension
    Traceback (most recent call last):
      File "/home/controller/jupyterhub/lib/python3.8/site-packages/jupyterhub/singleuser/extension.py", line 274, in wrapped
        r = f(self, *args, **kwargs)
      File "/home/controller/jupyterhub/lib/python3.8/site-packages/jupyterhub/singleuser/extension.py", line 472, in load_config_file
        raise KeyError("Missing required environment $JUPYTERHUB_SERVICE_URL")
    KeyError: 'Missing required environment $JUPYTERHUB_SERVICE_URL'

Cheers for the logs. Can you try spawning removing following config from your jupyterhub_config.py?

1 Like

Hi @mahendrapaipuri,

After removing those two configuration, I was able to run the JupyterHub as what I would expect. Is there a reason, why I can’t specify the IP and Port? Thanks for all the help and I really appreciate all the support.

1 Like

By setting a config like that you are saying to launch all single user servers on the same port 8886. Your first user’s single user server is already running at the port 8886 and when you attempted to lunch another single user server with different user, JupyterHub is redirecting your second user to the single user server of first user. But obviously the single user server of your second user never started due to port clash and hence 404.

Normally it is spawner’s responsbility to find a free port and lunch single user server on that port.

2 Likes