mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-08 02:24:08 +00:00
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
import json
|
|
|
|
from tornado import web
|
|
from tornado.httpclient import AsyncHTTPClient, HTTPClientError
|
|
from traitlets import Unicode
|
|
|
|
from jupyterhub.auth import Authenticator
|
|
from jupyterhub.utils import url_path_join
|
|
|
|
|
|
class ForcedLoginAuthenticator(Authenticator):
|
|
"""Authenticator to force login with a token provided by an external service
|
|
|
|
The external service issues tokens, which are exchanged for a username.
|
|
Visiting `/hub/login?login_token=...` logs in a user
|
|
Each token can be used only once.
|
|
"""
|
|
|
|
auto_login = True # begin login without prompt (token is in url)
|
|
allow_all = True # external login app controls this
|
|
token_provider_url = Unicode(
|
|
config=True, help="""The URL of the token/username provider"""
|
|
)
|
|
|
|
async def authenticate(self, handler, data):
|
|
token = handler.get_argument("login_token", None)
|
|
if not token:
|
|
raise web.HTTPError(
|
|
400, f"Login with external provider at {self.token_provider_url}"
|
|
)
|
|
client = AsyncHTTPClient()
|
|
try:
|
|
response = await client.fetch(
|
|
url_path_join(self.token_provider_url, "/login"),
|
|
method="POST",
|
|
headers={"Content-Type": "application/json"},
|
|
body=json.dumps({"token": token}),
|
|
)
|
|
except HTTPClientError as e:
|
|
self.log.info(
|
|
"Error exchanging token for username: %s",
|
|
e.response.body.decode("utf8", "replace"),
|
|
)
|
|
if e.code == 404:
|
|
raise web.HTTPError(
|
|
403,
|
|
f"Invalid token. Login with external provider at {self.token_provider_url}",
|
|
)
|
|
else:
|
|
raise
|
|
# pass through the response
|
|
return json.loads(response.body.decode())
|
|
|
|
|
|
c = get_config() # noqa
|
|
|
|
# use our Authenticator
|
|
c.JupyterHub.authenticator_class = ForcedLoginAuthenticator
|
|
# tell it where the external launch app is
|
|
c.ForcedLoginAuthenticator.token_provider_url = "http://127.0.0.1:9000/"
|
|
|
|
|
|
# local testing config (fake spawner, localhost only)
|
|
c.JupyterHub.ip = "127.0.0.1"
|
|
c.JupyterHub.spawner_class = "simple"
|