Hi Minrk, in my system, we use Jupyter Notebook 7 embedded in an iframe. I have a requirement to avoid the login screen from Jupyter, so the authentication came from our system; this authentication could be from a token in the header, a token in the URL, or a cookie. In my Authenticator, I define a custom login handler to deal with this:
def get_handlers(self, app: Any) -> List[Tuple[str, Type[RequestHandler]]]:
return [
(r"/login", JSONWebTokenLoginHandler),
]
I don’t override the authenticate because when I try that, I get the Jupyter login screen,
In that Login handler I got:
# Extract the JWT token from the request (header, URL argument, or cookie)
jwt_token = self._extract_jwt_token(handler)
# Validate the token
if self._validate_jwt_token(jwt_token):
claims = None
if secret:
claims = verify_jwt_using_secret(token, secret, audience)
if claims is None:
self.log.error("Invalid claims.")
raise web.HTTPError(401)
username = retrieve_username(claims, username_claim_field)
user = self.user_from_username(username)
self.log.info(f"Username from claims: {username}. User for set cookie: {user}")
# Clean cookie if the user is not correct
jupyterhub_cookie = self.get_current_user_cookie()
if jupyterhub_cookie:
cookie_user_name = jupyterhub_cookie.name
if cookie_user_name != username:
self.clear_cookie(self.hub.cookie_name, path=self.hub.base_url)
self.log.info(f'User cookie cleared: {cookie_user_name}')
self.set_login_cookie(user)
self.log.info(f'Request cookies: {self.request.cookies}')
_url = self.get_redirect_url(user, token)
self.log.info(f"Redirect to URL={_url}")
self.redirect(_url, permanent=False)
The iframe could be embedded in different web apps, and for each app, I got a different user that I used to create the server. The problem I got is that in the same web browser, a user can open a notebook from two different apps; this ends with the first notebook loading ok but the second one failing the auth because it is trying to open the server with the wrong cookie. To avoid this, I add this code:
jupyterhub_cookie = self.get_current_user_cookie()
if jupyterhub_cookie:
cookie_user_name = jupyterhub_cookie.name
if cookie_user_name != username:
self.clear_cookie(self.hub.cookie_name, path=self.hub.base_url)
self.log.info(f'User cookie cleared: {cookie_user_name}')
This ends with the Jupyterhub cookie removed but the authentication don’t pass because the redirect doesn’t have any valid cookie even when I add the self.set_login_cookie(user).
These are examples of the log when the auth is ok:
The server for user 82c20951-9587-4691-812d-7cd49b1a42f5 has been created.
[I 2024-03-20 09:51:24.994 JupyterHub authenticator:137] Header name: Authorization
QueryParam name: token
Header is auth: True
Header content:
Cookie content:
Hub cookie content:
Secret: secret-secret-secret
Username claim field: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
Audience:
Token param: **VALID JWT TOKEN**
[I 2024-03-20 09:51:24.996 JupyterHub authenticator:88] Username from claims: 82c20951-9587-4691-812d-7cd49b1a42f5. User for set cookie: <User(82c20951-9587-4691-812d-7cd49b1a42f5 1/2 running)>
[D 2024-03-20 09:51:24.996 JupyterHub base:591] Setting cookie for 82c20951-9587-4691-812d-7cd49b1a42f5: jupyterhub-hub-login
[D 2024-03-20 09:51:24.996 JupyterHub base:587] Setting cookie jupyterhub-hub-login: {'httponly': True, 'secure': True, 'SameSite': 'None', 'Secure': True, 'path': '/hub/'}
[I 2024-03-20 09:51:24.997 JupyterHub authenticator:99] Request cookies: Set-Cookie: .AspNetCore.Cookies=SOME VALUE
Set-Cookie: IsLoggedOrionUser=True
Set-Cookie: _xsrf=2|0ad9a02b|bdea3d7f6f852b65541a3a9e995d9025|1710501277
Set-Cookie: jupyterhub-session-id=b5b4aba54a7e4156b76cd69e07958fed
[W 2024-03-20 09:51:24.997 JupyterHub base:701] Redirecting /hub/login?token=VALID TOKEN&next=%2Fuser%2F82c20951-9587-4691-812d-7cd49b1a42f5%2Forionhubnsuser.0.25%2Fnotebooks%2F10%2Fdocument%2F5495%2FNew+Document+8862.ipynb&theme=theme-light to /hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New Document 8862.ipynb. For sharing public links, use /user-redirect/
[I 2024-03-20 09:51:24.997 JupyterHub authenticator:103] Redirect authentication to URL=/hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New Document 8862.ipynb?theme=theme-light?token=VALID TOKEN
[I 2024-03-20 09:51:24.998 JupyterHub log:191] 302 GET /hub/login?token=[secret]&next=%2Fuser%2F82c20951-9587-4691-812d-7cd49b1a42f5%2Forionhubnsuser.0.25%2Fnotebooks%2F10%2Fdocument%2F5495%2FNew+Document+8862.ipynb&theme=theme-light -> /hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New Document 8862.ipynb?theme=theme-light?token=VALID TOKEN (@192.168.65.4) 4.29ms
[D 2024-03-20 09:51:25.014 JupyterHub scopes:877] Checking access to /hub/api/users/82c20951-9587-4691-812d-7cd49b1a42f5/servers/orionhubnsuser.0.25/progress via scope read:servers
[D 2024-03-20 09:51:25.014 JupyterHub scopes:677] Unrestricted access to /hub/api/users/82c20951-9587-4691-812d-7cd49b1a42f5/servers/orionhubnsuser.0.25/progress via read:servers
[I 2024-03-20 09:51:25.016 JupyterHub users:725] Server 82c20951-9587-4691-812d-7cd49b1a42f5:orionhubnsuser.0.25 is already started
[I 2024-03-20 09:51:25.016 JupyterHub log:191] 200 GET /hub/api/users/82c20951-9587-4691-812d-7cd49b1a42f5/servers/orionhubnsuser.0.25/progress (orionendpoints@127.0.0.1) 17.69ms
[D 2024-03-20 09:51:25.063 JupyterHub scopes:877] Checking access to /hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New%20Document%208862.ipynb via scope access:servers
[D 2024-03-20 09:51:25.063 JupyterHub scopes:690] Argument-based access to /hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New%20Document%208862.ipynb via access:servers
[I 2024-03-20 09:51:25.064 JupyterHub log:191] 302 GET /hub/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New%20Document%208862.ipynb?theme=theme-light?token=VALID TOKEN -> /user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/notebooks/10/document/5495/New%20Document%208862.ipynb?theme=theme-light%3Ftoken%3DVALID TOKEN&redirects=1 (82c20951-9587-4691-812d-7cd49b1a42f5@192.168.65.4) 4.52ms
[D 2024-03-20 09:51:25.113 JupyterHub provider:420] Validating client id jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25
[D 2024-03-20 09:51:25.116 oauthlib.oauth2.rfc6749.grant_types.authorization_code authorization_code:362] Validating redirection uri /user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/oauth_callback for client jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25.
[D 2024-03-20 09:51:25.116 oauthlib.oauth2.rfc6749.grant_types.base base:231] Using provided redirect_uri /user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/oauth_callback
[D 2024-03-20 09:51:25.116 JupyterHub provider:495] validate_redirect_uri: client_id=jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25, redirect_uri=/user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/oauth_callback
[D 2024-03-20 09:51:25.119 oauthlib.oauth2.rfc6749.grant_types.base base:172] Validating access to scopes ['access:servers!server=82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25', 'read:users:groups!user', 'read:users:name!user'] for client 'jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25' (<OAuthClient(identifier='jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25')>).
[D 2024-03-20 09:51:25.120 JupyterHub provider:622] Allowing request for scope(s) for jupyterhub-user-82c20951-9587-4691-812d-7cd49b1a42f5-orionhubnsuser.0.25: read:users:groups!user,read:users:name!user,access:servers!server=82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25
[D 2024-03-20 09:51:25.122 JupyterHub auth:320] Skipping oauth confirmation for <User(82c20951-9587-4691-812d-7cd49b1a42f5 1/2 running)> accessing Server at /user/82c20951-9587-4691-812d-7cd49b1a42f5/orionhubnsuser.0.25/
These when fails:
[I 2024-03-20 10:30:25.295 JupyterHub authenticator:137] Header name: Authorization
QueryParam name: token
Header is auth: True
Header content:
Cookie content:
Hub cookie content: 2|1:0|10:1710929630|20:jupyterhub-hub-login|44:NzE5YTc3Mjc0OGY5NDkwZjkwZjg5Y2JlNzNjZjExYmE=|799c78956dec761f2b2010e8b704abbdf753c7f29bd7873dcad793c2c22aea3a
Secret: secret-secret-secret
Username claim field: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
Audience:
Token param: TOKEN
[I 2024-03-20 10:30:25.295 JupyterHub authenticator:76] Decoded token = TOKEN
[I 2024-03-20 10:30:25.297 JupyterHub authenticator:88] Username from claims: 70c3dbcc-a4e6-ee11-8010-186024ed73bd. User for set cookie: <User(70c3dbcc-a4e6-ee11-8010-186024ed73bd 1/2 running)>
[I 2024-03-20 10:30:25.297 JupyterHub authenticator:95] User cookie cleared: 1cefdbe5-a1e6-ee11-8010-186024ed73bd
[I 2024-03-20 10:30:25.298 JupyterHub authenticator:99] Request cookies: Set-Cookie: .AspNetCore.Cookies=COOKIE
Set-Cookie: IsLoggedOrionUser=True
Set-Cookie: _xsrf=2|0ad9a02b|bdea3d7f6f852b65541a3a9e995d9025|1710501277
Set-Cookie: jupyterhub-hub-login="2|1:0|10:1710929630|20:jupyterhub-hub-login|44:NzE5YTc3Mjc0OGY5NDkwZjkwZjg5Y2JlNzNjZjExYmE=|799c78956dec761f2b2010e8b704abbdf753c7f29bd7873dcad793c2c22aea34a"
Set-Cookie: jupyterhub-session-id=b5b4aba54a7e4156b76cd69e07958fed
[W 2024-03-20 10:30:25.298 JupyterHub base:701] Redirecting /hub/login?token=TOKEN=&next=/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process%20Tree%20-%20New%20Document%201831.ipynb to /hub/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process Tree - New Document 1831.ipynb. For sharing public links, use /user-redirect/
[I 2024-03-20 10:30:25.298 JupyterHub authenticator:103] Redirect authentication to URL=/hub/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process Tree - New Document 1831.ipynb?token=TOKEN
[I 2024-03-20 10:30:25.299 JupyterHub log:191] 302 GET /hub/login?token=[secret]&next=/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process%20Tree%20-%20New%20Document%201831.ipynb -> /hub/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process Tree - New Document 1831.ipynb?token=[secret] (1cefdbe5-a1e6-ee11-8010-186024ed73bd@192.168.65.4) 14.86ms
[I 2024-03-20 10:30:25.307 JupyterHub log:191] 302 GET /hub/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process%20Tree%20-%20New%20Document%201831.ipynb?token=[secret] -> /hub/login?next=%2Fhub%2Fuser%2F70c3dbcc-a4e6-ee11-8010-186024ed73bd%2Forionhubnsrigel.0.25%2Fnotebooks%2Fdocument%2F5497%2FProcess%2520Tree%2520-%2520New%2520Document%25201831.ipynb%3Ftoken%3TOKEN (@192.168.65.4) 1.37ms
[E 2024-03-20 10:30:25.312 JupyterHub authenticator:28] {'next': [b'/hub/user/70c3dbcc-a4e6-ee11-8010-186024ed73bd/orionhubnsrigel.0.25/notebooks/document/5497/Process%20Tree%20-%20New%20Document%201831.ipynb?token=TOKEN']}
[I 2024-03-20 10:30:25.312 JupyterHub authenticator:137] Header name: Authorization
QueryParam name: token
Header is auth: True
Header content:
Cookie content:
Hub cookie content:
Secret: secret-secret-secret
Username claim field: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
Audience:
Token param: False
[E 2024-03-20 10:30:25.312 JupyterHub authenticator:67] There is no available info to authenticate
[D 2024-03-20 10:30:25.313 JupyterHub base:1371] No template for 401
I think that the cookie that I set is not present in the current request that I’m validating on the login.
What I expect would be to the auth working as in the first request for the other user.