【问题标题】:Use Django OAuth2 provider with JupyterHub将 Django OAuth2 提供程序与 JupyterHub 一起使用
【发布时间】:2018-06-18 21:49:25
【问题描述】:

我正在尝试运行与 JupyterHub 服务器配对的 Django Web 应用程序,用户通过 Web 应用程序进入,然后在登录后被授予对笔记本服务器的访问权限。为此,我尝试使用 OAuth2,其中 Django 提供身份验证,而 JupyterHub 会根据此验证用户。

我正在使用django-oauth-toolkit 提供身份验证服务并使用通用 OAuthenticator 链接它。 A docker-compose reference implementation is available here。目前,授权重定向有效,但令牌检索过程的某些部分会引发以下错误:

jupyterhub_1  | [I 2018-01-07 18:53:41.763 JupyterHub log:124] 302 GET /hub/oauth_login?next= → http://localhost:8000/o/authorize?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback (@172.22.0.1) 3.98ms
django_1      | [07/Jan/2018 18:53:41] "GET /o/authorize?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback HTTP/1.1" 301 0
django_1      | [07/Jan/2018 18:53:41] "GET /o/authorize/?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback HTTP/1.1" 200 3159
django_1      | [07/Jan/2018 18:53:42] "POST /o/authorize/?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback HTTP/1.1" 302 0
jupyterhub_1  | [W 2018-01-07 18:53:42.959 JupyterHub log:124] 405 POST /o/token (@127.0.0.1) 9.08ms
jupyterhub_1  | [E 2018-01-07 18:53:42.961 JupyterHub web:1590] Uncaught exception GET /hub/oauth_callback?code=Rz9OLMKqO0QBne5evvJJjusEFjEhto&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D (172.22.0.1)
jupyterhub_1  |     HTTPServerRequest(protocol='http', host='localhost:8001', method='GET', uri='/hub/oauth_callback?code=Rz9OLMKqO0QBne5evvJJjusEFjEhto&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D', version='HTTP/1.1', remote_ip='172.22.0.1', headers={'X-Forwarded-Host': 'localhost:8001', 'Accept-Encoding': 'gzip, deflate, br', 'X-Forwarded-Port': '8001', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36', 'Upgrade-Insecure-Requests': '1', 'Cache-Control': 'max-age=0', 'Referer': 'http://localhost:8000/o/authorize/?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback', 'X-Forwarded-For': '172.22.0.1', 'X-Forwarded-Proto': 'http', 'Host': 'localhost:8001', 'Connection': 'close', 'Accept-Language': 'en-US,en;q=0.9', 'Cookie': '_xsrf=2|159cb5ee|fde7d35de59d079ff7b5b4e029156a50|1509298869; GUID_8800=6XuSYLOxGOHGBer7Ks3o; csrftoken=zDh7M6uxWbz8G83FZHPz29PBxRsj79m9x70bYc8PBOsOJAY3F9uNq60g2nHOpP56; sessionid=wfia2uydbydieqahawxlsk2rz45uhjoc; oauthenticator-state="2|1:0|10:1515351221|20:oauthenticator-state|120:ZXlKdVpYaDBYM1Z5YkNJNklDSWlMQ0FpYzNSaGRHVmZhV1FpT2lBaVlXUTBORGMzTUdWbVptWTVORE15T0dFek9EQmxOVGhqTUdJNVlXUTBaVGNpZlE9PQ==|c1315c25a514c4e01d49edeb1a0b4f9595c88b9f309ec041d31eca10c6510030"'})
jupyterhub_1  |     Traceback (most recent call last):
jupyterhub_1  |       File "/opt/conda/lib/python3.5/site-packages/tornado/web.py", line 1511, in _execute
jupyterhub_1  |         result = yield result
jupyterhub_1  |       File "/opt/conda/lib/python3.5/site-packages/oauthenticator/oauth2.py", line 182, in get
jupyterhub_1  |         user = yield self.login_user()
jupyterhub_1  |       File "/opt/conda/lib/python3.5/site-packages/jupyterhub/handlers/base.py", line 407, in login_user
jupyterhub_1  |         authenticated = yield self.authenticate(data)
jupyterhub_1  |       File "/opt/conda/lib/python3.5/site-packages/jupyterhub/auth.py", line 227, in get_authenticated_user
jupyterhub_1  |         authenticated = yield self.authenticate(handler, data)
jupyterhub_1  |       File "/opt/conda/lib/python3.5/site-packages/oauthenticator/generic.py", line 101, in authenticate
jupyterhub_1  |         resp = yield http_client.fetch(req)
jupyterhub_1  |     tornado.httpclient.HTTPError: HTTP 405: Method Not Allowed
jupyterhub_1  |     
jupyterhub_1  | [E 2018-01-07 18:53:42.965 JupyterHub log:116] {
jupyterhub_1  |       "X-Forwarded-Host": "localhost:8001",
jupyterhub_1  |       "Accept-Encoding": "gzip, deflate, br",
jupyterhub_1  |       "X-Forwarded-Port": "8001",
jupyterhub_1  |       "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
jupyterhub_1  |       "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36",
jupyterhub_1  |       "Upgrade-Insecure-Requests": "1",
jupyterhub_1  |       "Cache-Control": "max-age=0",
jupyterhub_1  |       "Referer": "http://localhost:8000/o/authorize/?client_id=5hICA5iNiBhBuROGzxGJqGQ7Ur7yH8dHi53aPLB5&response_type=code&state=eyJuZXh0X3VybCI6ICIiLCAic3RhdGVfaWQiOiAiYWQ0NDc3MGVmZmY5NDMyOGEzODBlNThjMGI5YWQ0ZTcifQ%3D%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8001%2Fhub%2Foauth_callback",
jupyterhub_1  |       "X-Forwarded-For": "172.22.0.1",
jupyterhub_1  |       "X-Forwarded-Proto": "http",
jupyterhub_1  |       "Host": "localhost:8001",
jupyterhub_1  |       "Connection": "close",
jupyterhub_1  |       "Accept-Language": "en-US,en;q=0.9",
jupyterhub_1  |       "Cookie": "_xsrf=2|159cb5ee|fde7d35de59d079ff7b5b4e029156a50|1509298869; GUID_8800=6XuSYLOxGOHGBer7Ks3o; csrftoken=zDh7M6uxWbz8G83FZHPz29PBxRsj79m9x70bYc8PBOsOJAY3F9uNq60g2nHOpP56; sessionid=wfia2uydbydieqahawxlsk2rz45uhjoc; oauthenticator-state=\"2|1:0|10:1515351221|20:oauthenticator-state|120:ZXlKdVpYaDBYM1Z5YkNJNklDSWlMQ0FpYzNSaGRHVmZhV1FpT2lBaVlXUTBORGMzTUdWbVptWTVORE15T0dFek9EQmxOVGhqTUdJNVlXUTBaVGNpZlE9PQ==|c1315c25a514c4e01d49edeb1a0b4f9595c88b9f309ec041d31eca10c6510030\""
jupyterhub_1  |     }

所有必要的代码和配置都可以通过上面的源链接获得。我是否错误地使用/配置了身份验证?我是否在某个底层库中遇到了错误(我特别怀疑 JupyterHub,因为它处于早期积极开发阶段)?

编辑:我已将其发布为an issue on the OAuthenticator github,但仍然没有得到任何帮助。我找不到其他人之前将这两个服务联系起来,但它们共享 OAuth2 协议,因此应该可以相互协作。我已经挖掘了 JupyterHub Generic OAuthenticator 和 Django OAuth2 提供程序的源代码,但无法弄清楚为什么会抛出这个错误。谁能帮我解决这个问题?

【问题讨论】:

    标签: python django oauth docker-compose jupyter


    【解决方案1】:

    结果是我必须解决一些小错误才能使其正常工作:

    1. 用于获取令牌的 URL 与 JupyterHub 服务器相关,而不是像授权 URL 那样与客户端/浏览器相关。在提供的 Docker Compose 示例中,django 身份验证服务器相对于客户端是“localhost:8000”,但相对于 JupyterHub 服务器是“django:8000”。
    2. 因此,适当的主机名(“localhost”和“django”)需要在 Django 应用的 ALLOWED_HOSTS 列表中。
    3. 我不肯定这是必要的,但我还添加了oauth-toolkit documentation建议的中间件:

      中间件 = [ ..., 'oauth2_provider.middleware.OAuth2TokenMiddleware', ]

    4. JupyterHub 还需要用户数据 URL 来获取用户名。这必须在 OAUTH2_USERDATA_URL 环境变量中提供(同样,使用相对于 JupyterHub 服务器的 URL),并且该 URL 必须返回一个 JSON blob,至少包含一个“用户名”键。

    从以前的代码到工作示例的完整差异可用at this commit(以及该存储库中的完整、最小示例)。

    【讨论】:

    • 感谢分享!您是否能够让多个用户访问单个笔记本,还是 1-1?
    • @HashRocketSyntax JupyterHub 的重点是为每个用户提供自己独立的 Notebook 服务器,所以是 1-1。您可能能够在这些服务器之间共享 .ipynb 文件,具体取决于您使用的生成器类型。如果您确实希望不同的用户共享同一个笔记本服务器,则需要在 OAuth 视图中放置一个 shim,以将这些用户表示到 Hub,就好像他们是同一个用户一样。
    • 感谢您的澄清。听起来我只需要一个笔记本服务器和指定笔记本文件路径的 user_notebooks 之间的关系
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 2016-08-27
    • 2017-05-11
    • 2012-07-27
    • 1970-01-01
    • 2020-10-24
    相关资源
    最近更新 更多