I'm setting up JupyterHub with OAuth2 authentication using Django and DockerSpawner. Here are my configurations:
Requirements:
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'
}
}
Clinet
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."}
Both JupyterHub and Django are running inside separate containers. As a result, using "http://localhost:6678/o/authorize/" won't work because the JupyterHub container is trying to access the server on port 6678 within its own container. Instead of using "localhost," you should use the hostname or service name to enable proper communication between the containers.