I’m setting up JupyterHub with OAuth2 authentication using Django and DockerSpawner. Here are my configurations:
Requirements:
- Django==4.2.13
- django-cors-headers==4.4.0
- django-oauth-toolkit==2.4.0
Settings
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'oauth2_provider',
'corsheaders',
'users.apps.UsersConfig',
]
MIDDLEWARE = [
# ...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
'corsheaders.middleware.CorsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
LOGIN_URL = '/admin/login/'
AUTHENTICATION_BACKENDS = (
'oauth2_provider.backends.OAuth2Backend',
'django.contrib.auth.backends.ModelBackend'
)
OAUTH2_PROVIDER = {
'OIDC_ENABLED': True,
'PKCE_REQUIRED': False,
'SCOPES': {
'openid': 'OpenID scope',
'read': 'Read scope',
'write': 'Write scope',
'groups': 'Access to your groups'
}
}
JupyterHub Config:
c = get_config()
c.JupyterHub.spawner_class = "dockerspawner.DockerSpawner"
c.DockerSpawner.image = 'jupyter/base-notebook:latest'
c.DockerSpawner.use_internal_ip = True
c.DockerSpawner.network_name = "jupyterhub-network"
c.DockerSpawner.notebook_dir = "/home/jovyan/work"
c.DockerSpawner.volumes = {"jupyterhub-user-{username}": "/home/jovyan/work"}
c.DockerSpawner.remove = True
c.DockerSpawner.debug = True
c.JupyterHub.hub_ip = "jupyterhub"
c.JupyterHub.hub_port = 8080
c.Authenticator.admin_users = ["admin"]
c.JupyterHub.authenticator_class = "oauthenticator.generic.GenericOAuthenticator"
# OAuth2 application info
c.GenericOAuthenticator.client_id = "Wg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ"
c.GenericOAuthenticator.client_secret = "Xu4ySuo4N06M0mYcNeElcuWIstGCll8ZKX87HAMhnJSkDnJB91tx5fKL5AMBbfhcQu6jA5p9TuNGy4k3bAsxoPGeBPl3JLD7dJuC95eBc1OGwxCQ4ZpRuM1wB2oGqVJ4"
c.GenericOAuthenticator.authorize_url = "http://localhost:6678/o/authorize/"
c.GenericOAuthenticator.token_url = "http://localhost:6678/o/token/"
c.GenericOAuthenticator.userdata_url = "http://localhost:6678/o/userinfo/"
c.OAuthenticator.oauth_callback_url = "http://localhost:8000/hub/oauth_callback"
c.GenericOAuthenticator.scope = ["read", "openid"]
c.GenericOAuthenticator.username_claim = "openid"
# Authorization
c.OAuthenticator.allow_all = True
c.GenericOAuthenticator.admin_users = {"admin"}
c.GenericOAuthenticator.admin_groups = {"administrator"}
Docker Compose
version: "3"
services:
jupyterhub:
build:
context: .
dockerfile: dockerfile.jupyterhub
args:
JUPYTERHUB_VERSION: latest
restart: always
container_name: jupyterhub
networks:
- jupyterhub-network
volumes:
- ./jupyterhub/config:/srv/jupyterhub
- ./jupyterhub/data:/data
- /var/run/docker.sock:/var/run/docker.sock
ports:
- "8000:8000"
depends_on:
- django
django:
build:
context: .
dockerfile: dockerfile
restart: always
container_name: django
networks:
- jupyterhub-network
volumes:
- ./:/app
ports:
- "6678:6678"
command: "python manage.py runserver 0.0.0.0:6678"
networks:
jupyterhub-network:
name: jupyterhub-network
Issue Encountered
When attempting to authenticate via JupyterHub, I receive the following error in JupyterHub logs:
jupyterhub | [I 2024-07-04 10:14:18.233 JupyterHub log:192] 302 GET / -> /hub/ (@::ffff:172.18.0.1) 0.72ms
jupyterhub | [I 2024-07-04 10:14:18.247 JupyterHub log:192] 302 GET /hub/ -> /hub/login?next=%2Fhub%2F (@::ffff:172.18.0.1) 1.07ms
jupyterhub | [I 2024-07-04 10:14:18.256 JupyterHub _xsrf_utils:125] Setting new xsrf cookie for b'None:3Hrw9x-QbdoVkcXssRMeCSN651Os8pYJg_Doe1px5Tc=' {'path': '/hub/', 'max_age': 3600}
jupyterhub | [I 2024-07-04 10:14:18.312 JupyterHub log:192] 200 GET /hub/login?next=%2Fhub%2F (@::ffff:172.18.0.1) 58.07ms
jupyterhub | [I 2024-07-04 10:14:19.816 JupyterHub oauth2:99] OAuth redirect: http://localhost:8000/hub/oauth_callback
jupyterhub | [I 2024-07-04 10:14:19.817 JupyterHub log:192] 302 GET /hub/oauth_login?next=%2Fhub%2F -> http://localhost:6678/o/authorize/?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fhub%2Foauth_callback&client_id=Wg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ&state=[secret]&scope=read+openid (@::ffff:172.18.0.1) 0.96ms
django | [04/Jul/2024 10:14:19] "GET /o/authorize/?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fhub%2Foauth_callback&client_id=Wg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ&state=eyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9&scope=read+openid HTTP/1.1" 302 0
django | [04/Jul/2024 10:14:19] "GET /admin/login/?next=/o/authorize/%3Fresponse_type%3Dcode%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8000%252Fhub%252Foauth_callback%26client_id%3DWg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ%26state%3DeyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9%26scope%3Dread%2Bopenid HTTP/1.1" 200 4693
django | [04/Jul/2024 10:14:19] "GET /static/admin/css/dark_mode.css HTTP/1.1" 200 2929
django | [04/Jul/2024 10:14:19] "GET /static/admin/css/nav_sidebar.css HTTP/1.1" 200 2694
django | [04/Jul/2024 10:14:19] "GET /static/admin/css/login.css HTTP/1.1" 200 958
django | [04/Jul/2024 10:14:19] "GET /static/admin/js/theme.js HTTP/1.1" 200 1943
django | [04/Jul/2024 10:14:19] "GET /static/admin/js/nav_sidebar.js HTTP/1.1" 200 3063
django | [04/Jul/2024 10:14:19] "GET /static/admin/css/base.css HTTP/1.1" 200 21310
django | [04/Jul/2024 10:14:19] "GET /static/admin/css/responsive.css HTTP/1.1" 200 18559
django | [04/Jul/2024 10:14:24] "POST /admin/login/?next=/o/authorize/%3Fresponse_type%3Dcode%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A8000%252Fhub%252Foauth_callback%26client_id%3DWg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ%26state%3DeyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9%26scope%3Dread%2Bopenid HTTP/1.1" 302 0
django | [04/Jul/2024 10:14:24] "GET /o/authorize/?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fhub%2Foauth_callback&client_id=Wg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ&state=eyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9&scope=read+openid HTTP/1.1" 200 3719
django | [04/Jul/2024 10:14:25] "POST /o/authorize/?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fhub%2Foauth_callback&client_id=Wg6aLxkQxt8D74FJ4U6vfOe781JeLNf1fUeeMKFJ&state=eyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9&scope=read+openid HTTP/1.1" 302 0
jupyterhub | [E 2024-07-04 10:14:25.433 JupyterHub oauth2:683] Error fetching 599 POST http://localhost:6678/o/token/: HTTP 599: Failed to connect to localhost port 6678 after 0 ms: Connection refused
jupyterhub | [E 2024-07-04 10:14:25.434 JupyterHub web:1875] Uncaught exception GET /hub/oauth_callback?code=yprl0rDglVnb1kJcIKAOzxy9Y5ty47&state=eyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9 (::ffff:172.18.0.1)
jupyterhub | HTTPServerRequest(protocol='http', host='localhost:8000', method='GET', uri='/hub/oauth_callback?code=yprl0rDglVnb1kJcIKAOzxy9Y5ty47&state=eyJzdGF0ZV9pZCI6ICJmYTYxNDE1ZTE4OWY0OGRlYTFlYzI3MDI5YWFkNTVlMCJ9', version='HTTP/1.1', remote_ip='::ffff:172.18.0.1')
jupyterhub | Traceback (most recent call last):
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/tornado/web.py", line 1790, in _execute
jupyterhub | result = await result
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 210, in get
jupyterhub | user = await self.login_user()
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/jupyterhub/handlers/base.py", line 964, in login_user
jupyterhub | authenticated = await self.authenticate(data)
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/jupyterhub/auth.py", line 661, in get_authenticated_user
jupyterhub | authenticated = await maybe_future(self.authenticate(handler, data))
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 1061, in authenticate
jupyterhub | token_info = await self.get_token_info(handler, access_token_params)
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 906, in get_token_info
jupyterhub | token_info = await self.httpfetch(
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 718, in httpfetch
jupyterhub | return await self.fetch(
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 684, in fetch
jupyterhub | raise e
jupyterhub | File "/usr/local/lib/python3.10/dist-packages/oauthenticator/oauth2.py", line 663, in fetch
jupyterhub | resp = await self.http_client.fetch(req, **kwargs)
jupyterhub | tornado.curl_httpclient.CurlError: HTTP 599: Failed to connect to localhost port 6678 after 0 ms: Connection refused
jupyterhub |
jupyterhub | [E 2024-07-04 10:14:25.465 JupyterHub log:184] {
jupyterhub | "X-Forwarded-Host": "localhost:8000",
jupyterhub | "X-Forwarded-Proto": "http",
jupyterhub | "X-Forwarded-Port": "8000",
jupyterhub | "X-Forwarded-For": "::ffff:172.18.0.1",
jupyterhub | "Cookie": "_xsrf=[secret]; oauthenticator-state=[secret]; csrftoken=[secret]; sessionid=[secret]",
jupyterhub | "Accept-Encoding": "gzip, deflate, br, zstd",
jupyterhub | "Accept-Language": "en-GB,en",
jupyterhub | "Sec-Ch-Ua-Platform": ""Linux"",
jupyterhub | "Sec-Ch-Ua-Mobile": "?0",
jupyterhub | "Sec-Ch-Ua": ""Not/A)Brand";v="8", "Chromium";v="126", "Brave";v="126"",
jupyterhub | "Sec-Fetch-Dest": "document",
jupyterhub | "Sec-Fetch-User": "?1",
jupyterhub | "Sec-Fetch-Mode": "navigate",
jupyterhub | "Sec-Fetch-Site": "same-site",
jupyterhub | "Sec-Gpc": "1",
jupyterhub | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
jupyterhub | "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
jupyterhub | "Upgrade-Insecure-Requests": "1",
jupyterhub | "Cache-Control": "max-age=0",
jupyterhub | "Connection": "keep-alive",
jupyterhub | "Host": "localhost:8000"
jupyterhub | }
jupyterhub | [E 2024-07-04 10:14:25.465 JupyterHub log:192] 500 GET /hub/oauth_callback?code=[secret]&state=[secret] (@::ffff:172.18.0.1) 32.33ms
I’m seeking guidance on resolving the connection issue between JupyterHub and the OAuth2 server running on Django. Specifically, how can I ensure JupyterHub correctly communicates with the Django OAuth server for successful authentication?
Also, Work fine in postman while authetication but didn’t get any userinfo at link/o/userinfo/
{"error": "invalid_token", "error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons."}