    def get_login_url(self):
        """Return the Hub's login URL"""
        if self._hub_login_url is not None:
            # cached value, don't call this more than once per handler
            return self._hub_login_url

        if not self.hub_auth.login_url:
            # HubOAuth is required for login via redirect,
            # base class can only raise to avoid redirect loops
            raise HTTPError(403)

        # temporary override at setting level,
        # to allow any subclass overrides of get_login_url to preserve their effect
        # for example, APIHandler raises 403 to prevent redirects
        with mock.patch.dict(
            self.application.settings, {"login_url": self.hub_auth.login_url}
        ):
            login_url = super().get_login_url()
        app_log.debug("Redirecting to login url: %s", login_url)

        if isinstance(self.hub_auth, HubOAuth):
            # add state argument to OAuth url
            # must do this _after_ allowing get_login_url to raise
            # so we don't set unused cookies
            state = self.hub_auth.set_state_cookie(self, next_url=self.request.uri)
            login_url = url_concat(login_url, {'state': state})
        self._hub_login_url = login_url
        return login_url
Return the Hub's login URL
jupyterhub/services/auth.py