Context:
- I’m creating a Dashboard service where multiple users can access multiple notebook servers via
JupyterHub
. - Users log in using a separate Authentication service, and once in Dashboard ,
JupyterHub
gets rendered inside an<iframe>
.
Setup:
- I’m hosting
JupyterHub
in aKubernetes
cluster on GCP. These are the relevant portions of myconfig.yaml
:
...
# Changes the Jupyter's notebook headers to allow it to be displayed
# inside of an iframe object.
c.JupyterHub.tornado_settings = {
"headers": {
"Content-Security-Policy": "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval' 'unsafe-dynamic'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';",
}
}
...
extraEnv:
#Endpoints
OAUTH2_AUTHORIZE_URL: "http://35.232.66.179:8000/o/authorize"
OAUTH2_TOKEN_URL: "http://35.232.66.179:8000/o/token"
# Jupyterhub callback (default: https://<host>/hub/oauth_callback)
OAUTH_CALLBACK_URL: "http://35.196.54.180/hub/oauth_callback"
# User information is used to name server created by kubespawner.
OAUTH2_USERDATA_URL: "http://35.232.66.179:8000/userdata"
OAUTH2_USERNAME_KEY: "username"
OAUTH2_USERDATA_METHOD: "GET"
# General settings
OAUTH2_BASIC_AUTH: "false"
OAUTH2_TLS_VERIFY: "true"
...
auth:
type: custom
custom:
className: oauthenticator.generic.GenericOAuthenticator
config:
login_service: "OpenID"
client_id: "<client_id>"
client_secret: "<client_secret>"
Errors
However, when I try to sign in just via my JupyterHub
endpoint I get: 500 Internal Server Error
logs:
IwN2YyNzVhNTk0MzM0ZTM5YmIyZTdmZDQzY2U2ZDI3YyIsICJuZXh0X3VybCI6IG51bGx9&session_state=641fb9aca6490d2c3332871d72ee0639b3ed46b8a9f02c363ee8f7bf46f1c989.74e37d534296
cad45421019d1d442153', version='HTTP/1.1', remote_ip='<>')
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/tornado/web.py", line 1703, in _execute
result = await result
File "/usr/local/lib/python3.6/dist-packages/oauthenticator/oauth2.py", line 213, in get
user = await self.login_user()
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/handlers/base.py", line 699, in login_user
authenticated = await self.authenticate(data)
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/auth.py", line 383, in get_authenticated_user
authenticated = await maybe_future(self.authenticate(handler, data))
File "/usr/local/lib/python3.6/dist-packages/oauthenticator/generic.py", line 136, in authenticate
resp_json = json.loads(resp.body.decode('utf8', 'replace'))
File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 5 column 1 (char 4)
I know it actually does redirect to the Authentication Server and the callback is correct. Even still when I attempt to access via Dashboard , within the <iframe>
I get:
400 OAuth state missing from cookies
logs:
400 GET /hub/oauth_callback?code=d37de16a855a49dda94fb65c2c2c8ffa&state=eyJzdGF0ZV9pZCI6ICIwN2YyNzVhNTk0MzM0ZTM5Ym
IyZTdmZDQzY2U2ZDI3YyIsICJuZXh0X3VybCI6IG51bGx9&session_state=641fb9aca6490d2c3332871d72ee0639b3ed46b8a9f02c363ee8f7bf46f1c989.74e37d534296cad45421019d1d442153
OAuth state missing from cookies
Reference
I previously used the Github OAuthenticator
class and registered an OAuth app within Github and got JupyterHub
to work fine. However, Github does not allow their apps to be used within an <iframe>
so I had to go the generic route.
Point is, the setup was very similar and at least had JupyterHub
working outside of Dashboard , so I’m really stumped on what else I could be missing. Any help is appreciated.