diff --git a/jupyterhub/handlers/pages.py b/jupyterhub/handlers/pages.py index 734c257d..e0a7ddfb 100644 --- a/jupyterhub/handlers/pages.py +++ b/jupyterhub/handlers/pages.py @@ -67,9 +67,6 @@ class HomeHandler(BaseHandler): else: url = url_path_join(self.hub.base_url, 'spawn', user.escaped_name) - auth_state = await user.get_auth_state() - user.spawner.run_auth_state_hook(auth_state) - html = self.render_template( 'home.html', user=user, @@ -149,6 +146,7 @@ class SpawnHandler(BaseHandler): server_name = '' spawner = user.spawners[server_name] + # resolve `?next=...`, falling back on the spawn-pending url # must not be /user/server for named servers, # which may get handled by the default server if they aren't ready yet @@ -175,6 +173,12 @@ class SpawnHandler(BaseHandler): # Add handler to spawner here so you can access query params in form rendering. spawner.handler = self + + # auth_state may be an input to options form, + # so resolve the auth state hook here + auth_state = await user.get_auth_state() + await spawner.run_auth_state_hook(auth_state) + spawner_options_form = await spawner.get_options_form() if spawner_options_form: self.log.debug("Serving options form for %s", spawner._log_name) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 4639cb69..438fe7d1 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -972,11 +972,11 @@ class Spawner(LoggingConfigurable): except Exception: self.log.exception("post_stop_hook failed with exception: %s", self) - def run_auth_state_hook(self, auth_state): + async def run_auth_state_hook(self, auth_state): """Run the auth_state_hook if defined""" if self.auth_state_hook is not None: try: - return self.auth_state_hook(self, auth_state) + await maybe_future(self.auth_state_hook(self, auth_state)) except Exception: self.log.exception("auth_stop_hook failed with exception: %s", self) diff --git a/jupyterhub/user.py b/jupyterhub/user.py index 4fca536a..d6419639 100644 --- a/jupyterhub/user.py +++ b/jupyterhub/user.py @@ -535,12 +535,17 @@ class User: # trigger pre-spawn hook on authenticator authenticator = self.authenticator try: + spawner._start_pending = True + if authenticator: # pre_spawn_start can thow errors that can lead to a redirect loop # if left uncaught (see https://github.com/jupyterhub/jupyterhub/issues/2683) await maybe_future(authenticator.pre_spawn_start(self, spawner)) - spawner._start_pending = True + # trigger auth_state hook + auth_state = await self.get_auth_state() + await spawner.run_auth_state_hook(auth_state) + # update spawner start time, and activity for both spawner and user self.last_activity = ( spawner.orm_spawner.started